-
Notifications
You must be signed in to change notification settings - Fork 5.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose getter for nameAttributeKey in OAuth2AuthenticatedPrincipal #16003
base: main
Are you sure you want to change the base?
Expose getter for nameAttributeKey in OAuth2AuthenticatedPrincipal #16003
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @andreblanke! I've left some feedback inline.
.../main/java/org/springframework/security/oauth2/core/DefaultOAuth2AuthenticatedPrincipal.java
Outdated
Show resolved
Hide resolved
.../main/java/org/springframework/security/oauth2/core/DefaultOAuth2AuthenticatedPrincipal.java
Outdated
Show resolved
Hide resolved
Hi, @andreblanke! Are you able to make the requested changes? No rush, if I don't hear from you in about a week, I'm happy to make the changes myself. |
c7bcf86
to
c92a4a0
Compare
c92a4a0
to
25f8bec
Compare
Hi there @jzheaux. First of all thank you for your feedback. Sorry, I've been postponing this. I'd like to finish the changes in the next few days once we've decided on an implementation (getter for |
4848c6b
to
6b815d2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the updates, @andreblanke! I've left some feedback inilne.
...main/java/org/springframework/security/oauth2/client/oidc/userinfo/OidcUserRequestUtils.java
Outdated
Show resolved
Hide resolved
@@ -96,7 +96,7 @@ public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2Authentic | |||
OAuth2AccessToken token = userRequest.getAccessToken(); | |||
Map<String, Object> attributes = this.attributesConverter.convert(userRequest).convert(response.getBody()); | |||
Collection<GrantedAuthority> authorities = getAuthorities(token, attributes, userNameAttributeName); | |||
return new DefaultOAuth2User(authorities, attributes, userNameAttributeName); | |||
return new DefaultOAuth2User(attributes.get(userNameAttributeName).toString(), attributes, authorities); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it guaranteed at this point that attribute.get(userNameAttributeName) is non-null? If not, please do a null check so that the code can offer a more informative error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, attributes
is nullable and an assertion is appropriate here, basically what my newly introduced DefaultOAuth2User.getNameFromAttributes
is doing.
However, I can't help but feel that "get name from attributes using nameAttributeKey with assertions" is a frequent enough operation (4 mostly similar occurrences have been touched by this PR) to maybe warrant a separate method.
What would you think about changing the constructors to use name
instead of nameAttributeKey
(as done by the PR) but introducing static factory methods for DefaultOidcUser
and DefaultOAuth2User
taking nameAttributeKey
as parameter instead of name
?
That would get rid of the old, inconsistent constructors (in terms of both the parameter order and the usage of nameAttributeKey
instead of name
) while giving a clear upgrade path. Additionally, OidcUserAuthority.collectClaims
mentioned above would not need to be duplicated or made accessible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think that could work. Perhaps a builder in that case, though, as that's more typical of Spring Security classes. It's also a bit more resilient against member additional member variables getting added over time.
Perhaps:
public static DefaultOAuth2User.Builder withNameAttributeKey(String nameAttributeKey);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added builders for DefaultOAuth2User
and DefaultOidcUser
. Right now the new "canonical constructor" is still public, but it might be better to make it private to be in line with, e.g., OAuth2AuthorizeRequest
?
* @param authorities the authorities granted to the user | ||
*/ | ||
public DefaultOidcUser(OidcIdToken idToken, Collection<? extends GrantedAuthority> authorities) { | ||
this(null, idToken, authorities); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this correct? It seems like it should be idToken.getAttribute(IdTokenClaimNames.SUB)
.
Same question for DefaultOidcUser(OidcIdToken, OidcUserInfo, Collection<? extends GrantedAuthority>)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both constructors ultimately delegate to
public DefaultOAuth2User(String name, Map<String, Object> attributes,
Collection<? extends GrantedAuthority> authorities) {
// ...
this.name = (name != null) ? name : (String) this.attributes.get("sub");
}
which is consistent with the constructor behavior of DefaultOAuth2AuthenticatedPrincipal
, although using (nevermind, OAuth2 core classes should likely not access OpenId Connect-related classes).IdTokenClaimNames.SUB
instead of "sub"
would be more appropriate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, for that to succeed I'd still need to remove the Assert.notNull(name, "name cannot be null");
from the constructor which is still present at the moment.
@JsonCreator | ||
DefaultOidcUserMixin(@JsonProperty("authorities") Collection<? extends GrantedAuthority> authorities, | ||
@JsonProperty("idToken") OidcIdToken idToken, @JsonProperty("userInfo") OidcUserInfo userInfo, | ||
@JsonProperty("nameAttributeKey") String nameAttributeKey) { | ||
} | ||
|
||
@JsonCreator | ||
DefaultOidcUserMixin(@JsonProperty("name") String name, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will you please add DefaultOAuth2UserMixinTests
and DefaultOidcUserMixinTests
to ensure that serializations of either arrangement can correctly deserialize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems that Jackson only supports a single @JsonCreator
for each class, meaning the backwards compatibility would be a bit more involved (perhaps using a custom deserializer?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, the next evolution of that mixin would be to use a deserializer.
Accepts null name for consistency with DefaultOAuth2AuthenticatedPrincipal. The private static method DefaultOAuth2User.getNameFromAttributes was added in order to keep assertions about nameAttributeKey and the looked up name for the deprecated constructor.
6b815d2
to
ad945b6
Compare
ad945b6
to
ced9228
Compare
Given a
DefaultOidcUser
orDefaultOAuth2User
, it is currently not possible to create a faithful copy of the instance since the constructors require anameAttributeKey
for the all-arg constructor:which is not accessible.
This issue has been mentioned before in #14461 (comment) where a custom
ExtendedOidcUser
decorator class is used to work around it. While that works, I feel that this should be possible without introducing a separate class using just the public API.The PR aims to change this by adding the
OAuth2AuthenticatedPrincipal.getNameAttributeKey
method in 464b078. This is a breaking change for classes implementing the interface.With the
nameAttributeKey
now available from within the interface, I figured it also makes sense to provide a default implementation forOAuth2AuthenticatedPrincipal.getName
in c7bcf86.