-
Notifications
You must be signed in to change notification settings - Fork 6k
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
[Azure Oauth2] IllegalArgumentException: Attribute value for "xxx" is null #16340
Comments
Code failing on Spring Security side: public DefaultOAuth2User(Collection<? extends GrantedAuthority> authorities, Map<String, Object> attributes,
String nameAttributeKey) {
Assert.notEmpty(attributes, "attributes cannot be empty");
Assert.hasText(nameAttributeKey, "nameAttributeKey cannot be empty");
Assert.notNull(attributes.get(nameAttributeKey),
"Attribute value for '" + nameAttributeKey + "' cannot be null");
this.authorities = (authorities != null)
? Collections.unmodifiableSet(new LinkedHashSet<>(this.sortAuthorities(authorities)))
: Collections.unmodifiableSet(new LinkedHashSet<>(AuthorityUtils.NO_AUTHORITIES));
this.attributes = Collections.unmodifiableMap(new LinkedHashMap<>(attributes));
this.nameAttributeKey = nameAttributeKey;
} If we use "preferred_username" as attribute:
If we use "sub" as name attribute key, we are able to proceed further into our @Override
public void onAuthenticationSuccess(
final HttpServletRequest request,
final HttpServletResponse response,
final Authentication authentication) throws IOException {
final OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
final String registrationId = token.getAuthorizedClientRegistrationId();
final OAuth2User p = token.getPrincipal();
final Pair<String, String> pair = repository.getUsernameSuffixAndAllowedDomains(registrationId);
final String username = lowerCase(getUsername(p, pair.getKey()));
log.error(
"[SSO] id='{}', username='{}', attrs='{}', domains='{}'",
registrationId, username, p.getAttributes(), allowedDomains
);
....
} Where we can see that all attributes are there:
That means, "preferred_username" is an attribute but it's not working, despite being there. |
I have more info about the issue after debugging. The issue is in @Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
Assert.notNull(userRequest, "userRequest cannot be null");
OidcUserInfo userInfo = null;
if (this.retrieveUserInfo.test(userRequest)) {
OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest);
Map<String, Object> claims = getClaims(userRequest, oauth2User);
userInfo = new OidcUserInfo(claims);
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
// 1) The sub (subject) Claim MUST always be returned in the UserInfo Response
if (userInfo.getSubject() == null) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
// 2) Due to the possibility of token substitution attacks (see Section
// 16.11),
// the UserInfo Response is not guaranteed to be about the End-User
// identified by the sub (subject) element of the ID Token.
// The sub Claim in the UserInfo Response MUST be verified to exactly match
// the sub Claim in the ID Token; if they do not match,
// the UserInfo Response values MUST NOT be used.
if (!userInfo.getSubject().equals(userRequest.getIdToken().getSubject())) {
OAuth2Error oauth2Error = new OAuth2Error(INVALID_USER_INFO_RESPONSE_ERROR_CODE);
throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString());
}
}
return this.oidcUserMapper.apply(userRequest, userInfo);
} First, the Oauth2 user is created using OAuth2User oauth2User = this.oauth2UserService.loadUser(userRequest); In this case, the user has only limited attributes (4 in our case, like "sub"). Later on: return this.oidcUserMapper.apply(userRequest, userInfo); Which delegates to: org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequestUtils#getUser This is where the The bug is in Line 72 in 8e2a4bf
Assert.notNull(attributes.get(nameAttributeKey),
"Attribute value for '" + nameAttributeKey + "' cannot be null"); Checking for The bug has been introduced in this commit: This check cannot be performed this early. |
….getName() Signed-off-by: jloisel <[email protected]>
Hi, This bug is preventing us from using Oauth2 authentication. I suspect it's breaking any oauth2 integration which has the username-attribute claim provided by the userinfo endpoint. Can you please take a look? Best Regards, |
Associated pull request: #16546 |
@jloisel thanks for following up on this, and apologies that we didn't see this earlier.
I have personally used the Can you please put together a minimal reproducer that includes your Spring Security |
Hi, I understand you are probably quite busy! You will have to find a provider which doesn't provide the user-name-attribute in in the first claims. I don't know which other SSO provider works that way. We also have a Google SSO integration and this one works well (but all claims are provided by the first call). You don't even need any specific Spring Security configuration. You can use a sample SSO, as long as you use Azure SSO (maybe other SSO providers working same way as Azure are broken too). application.yml: (use your own clientId / clientSecret and TENANT_ID) spring.security.oauth2.client:
registration:
azure:
client-id: XXX
client-secret: XXX
name: "Azure"
scope: openid,profile,email
client-authentication-method: client_secret_basic
authorization-grant-type: authorization_code
redirect-uri: "{baseUrl}/{action}/oauth2/code/{registrationId}"
client-name: azure
provider:
azure:
authorization-uri: "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/authorize"
token-uri: "https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token"
user-info-uri: "https://graph.microsoft.com/oidc/userinfo"
jwk-set-uri: "https://login.microsoftonline.com/TENANT_ID/discovery/v2.0/keys"
issuer-uri: "https://login.microsoftonline.com/TENANT_ID/v2.0"
user-name-attribute: preferred_username You can use the default login page and try to login. It will fail with a null pointer if the user-name-attribute is not provided in the first claims. |
If I can't get a developer account, I often just write integrations tests or customize Spring Authorization Server to emulate the misbehaving or problematic provider. I can handle that no problem. Thanks for the config. |
Hi,
We are having the following issue when trying to configure Azure SSO Authentication using Spring Security Oauth2:
Seems linked to #15338.
If we use "sub" username attribute, there is no longer the issue (but sub cannot be mapped to an email on our system), but we can dump the user in an
Oauth2AuthenticationSuccessHandler
:The claims have "preferred_username" in
Oauth2AuthenticationSuccessHandler
but if we use any asuser-name-attribute
it fails with a null pointer exception (despite being in the user claims afterwards).I believe the null check is done too early and claims seem to be filled afterwards.
We are using Spring Security 6.4.2, Spring Boot 3.4.1.
The text was updated successfully, but these errors were encountered: