diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java index 09bf6929f55..49420e00542 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -99,6 +99,23 @@ public DefaultOidcUser(Collection authorities, OidcI this.userInfo = userInfo; } + DefaultOidcUser(DefaultOidcUser user, Collection authorities) { + super(user, authorities); + this.idToken = user.idToken; + this.userInfo = user.userInfo; + } + + /** + * Copy this {@code DefaultOAuth2User}, using the provided {@code authorities} + * @param authorities the authorities to use + * @return a new {@code DefaultOAuth2User} + * @since 6.5 + */ + @Override + public DefaultOidcUser withAuthorities(Collection authorities) { + return new DefaultOidcUser(this, authorities); + } + @Override public Map getClaims() { return this.getAttributes(); diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java index 6c80d7b64a2..9677ec8b565 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/user/DefaultOAuth2User.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -79,6 +79,12 @@ public DefaultOAuth2User(Collection authorities, Map this.nameAttributeKey = nameAttributeKey; } + protected DefaultOAuth2User(DefaultOAuth2User copy, Collection authorities) { + this.nameAttributeKey = copy.nameAttributeKey; + this.attributes = copy.attributes; + this.authorities = sortAuthorities(authorities); + } + @Override public String getName() { return this.getAttribute(this.nameAttributeKey).toString(); @@ -94,6 +100,16 @@ public Map getAttributes() { return this.attributes; } + /** + * Copy this {@code DefaultOAuth2User}, using the provided {@code authorities} + * @param authorities the authorities to use + * @return a new {@code DefaultOAuth2User} + * @since 6.5 + */ + public DefaultOAuth2User withAuthorities(Collection authorities) { + return new DefaultOAuth2User(this, authorities); + } + private Set sortAuthorities(Collection authorities) { SortedSet sortedAuthorities = new TreeSet<>( Comparator.comparing(GrantedAuthority::getAuthority)); diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java index 4c4ae825005..57cd2d0e50b 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/oidc/user/DefaultOidcUserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,8 @@ package org.springframework.security.oauth2.core.oidc.user; import java.time.Instant; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -31,6 +33,7 @@ import org.springframework.security.oauth2.core.oidc.OidcIdToken; import org.springframework.security.oauth2.core.oidc.OidcUserInfo; import org.springframework.security.oauth2.core.oidc.StandardClaimNames; +import org.springframework.security.oauth2.core.user.DefaultOAuth2User; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -147,4 +150,15 @@ public void constructorWhenAllParametersProvidedAndValidThenCreated() { StandardClaimNames.NAME, StandardClaimNames.EMAIL); } + @Test + public void constructorWhenWithAuthoritiesThenReplaces() { + DefaultOidcUser user = new DefaultOidcUser(AUTHORITIES, ID_TOKEN, USER_INFO); + Collection additional = new ArrayList<>(AUTHORITIES); + additional.add(new SimpleGrantedAuthority("ROLE_ADMIN")); + DefaultOAuth2User copy = user.withAuthorities(additional); + assertThat((Set) user.getAuthorities()).containsAll(AUTHORITIES); + assertThat((Set) copy.getAuthorities()).containsAll(additional); + assertThat(copy.getAttributes()).isEqualTo(user.getAttributes()); + } + } diff --git a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java index a56c5bcf6a2..0f322ff78cb 100644 --- a/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java +++ b/oauth2/oauth2-core/src/test/java/org/springframework/security/oauth2/core/user/DefaultOAuth2UserTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,8 @@ package org.springframework.security.oauth2.core.user; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -109,4 +111,15 @@ public void constructorWhenCreatedThenIsSerializable() { SerializationUtils.serialize(user); } + @Test + public void constructorWhenWithAuthoritiesThenReplaces() { + DefaultOAuth2User user = new DefaultOAuth2User(AUTHORITIES, ATTRIBUTES, ATTRIBUTE_NAME_KEY); + Collection additional = new ArrayList<>(AUTHORITIES); + additional.add(new SimpleGrantedAuthority("ROLE_ADMIN")); + DefaultOAuth2User copy = user.withAuthorities(additional); + assertThat((Set) user.getAuthorities()).containsAll(AUTHORITIES); + assertThat((Set) copy.getAuthorities()).containsAll(additional); + assertThat(copy.getAttributes()).isEqualTo(user.getAttributes()); + } + }