Skip to content

Userinfo endpoint with OAuth2 requires openid scope #918

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

Closed
mpsanchis opened this issue Oct 3, 2022 · 9 comments
Closed

Userinfo endpoint with OAuth2 requires openid scope #918

mpsanchis opened this issue Oct 3, 2022 · 9 comments
Assignees
Labels
status: invalid An issue that we don't feel is valid

Comments

@mpsanchis
Copy link

I have read @jgrandja 's comment in this thread and understood that if OIDC is not used (plain OAuth2), then /userinfo will always be called.
In the documentation I have read how to enable the /userinfo endpoint, by making the Auth Server (AS) also a Resource Server, and having a JwtDecoder bean.

My issue is that Spring's AS is using the OidcUserInfoAuthenticationProvider to authenticate the request to /userinfo, and therefore requires openid to be a scope (otherwise throws an exception signaling insufficient scope). But this means that the token is used for OIDC, and I was working on a simple app that would use only OAuth2.

By the threads that I have read, I assume that preventing the client from accessing /userinfo is not an option. Is there a way of configuring the AS in a way that it authorizes the call for non-OIDC scopes? I thought this would be available out-of-the-box, since OAuth2 should be a simpler protocol than OIDC.

Thank you in advance, and sorry if the formatting is not right. This is my first time opening an issue on GitHub. Let me know if I should change something.

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Oct 3, 2022
@sjohnr
Copy link
Member

sjohnr commented Oct 3, 2022

@mpsanchis, thanks for your interest in the project!

My issue is that Spring's AS is using the OidcUserInfoAuthenticationProvider to authenticate the request to /userinfo, and therefore requires openid to be a scope (otherwise throws an exception signaling insufficient scope).

You're correct that this is currently the behavior. The UserInfo endpoint has been implemented specific to OpenID Connect.

But this means that the token is used for OIDC, and I was working on a simple app that would use only OAuth2.

I believe it would always be recommended to use OIDC for authentication, as it was designed to address specific issues with using OAuth2 for authentication. Having said that, you're right that Spring Security includes support for it by utilizing the UserInfo endpoint as mandatory in the case of OAuth2 only.

Is there a way of configuring the AS in a way that it authorizes the call for non-OIDC scopes?

Other than providing a custom AuthenticationProvider (which is possible but not ideal in this case), I'm not aware of an easy way to set this up. We can definitely look into providing support for this. Thanks for pointing it out!

@sjohnr sjohnr added type: enhancement A general enhancement and removed status: waiting-for-triage An issue we've not yet triaged labels Oct 3, 2022
@mpsanchis
Copy link
Author

mpsanchis commented Oct 4, 2022

I will proceed with OIDC in that case. Thank you very much for the quick response! 😄

--
Just as a note for people that might get to this thread, as of now (Oct 04, 2022) the out-of-the-box behavior with Spring client and Spring AS is:

  • openid claim not added -> OAuth2 flow is used -> client calls /userinfo -> AS verifies that openid claim is missing -> AS throws an exception (this is a confusing flow, since not adding the claim in the client implies expecting it in the AS)

  • openid claim added -> OIDC flow is used -> client only calls /userinfo if some conditions are met (see here) -> if called, AS works because openid claim is present. If not called, user info will be extracted from ID token

@jgrandja
Copy link
Collaborator

jgrandja commented Oct 4, 2022

@mpsanchis The UserInfo Endpoint is specified in OpenID Connect Core 1.0. There is no equivalent in standard OAuth 2 because OAuth 2 is not intended to be used for User Authentication. OpenID Connect 1.0 is built on top of OAuth 2 to address User Authentication. Please review the Authorization Code Flow Steps for details on how this flow works.

I'm going to close this as the UserInfo Endpoint is specifically implemented for OpenID Connect and not meant to be used in standard OAuth 2 flows.

@jgrandja jgrandja closed this as completed Oct 4, 2022
@jgrandja jgrandja self-assigned this Oct 4, 2022
@jgrandja jgrandja added status: invalid An issue that we don't feel is valid and removed type: enhancement A general enhancement labels Oct 4, 2022
@mpsanchis
Copy link
Author

mpsanchis commented Oct 6, 2022

Dear @jgrandja ,
Thank you for the response.

I understand that userinfo endpoint is for OIDC, not OA2. However my client is built with Spring Security, with the basic oauth2Login and oauth2Client in the configuration class, and with spring.security.oauth2.client.registration in the yaml containing id, secret, scope, auth code grant type, redirect uri, and issuer uri. No extra logic, and still the client calls the /userinfo endpoint, as you mentioned in a comment years ago.

I am confused with what you mentioned back then:

The user-info-uri is required for standard OAuth 2.0 Providers

and what you are saying now:

... the UserInfo Endpoint is [...] not meant to be used in standard OAuth 2 flows.

My understanding is that without OIDC in the scopes, my client should not call this endpoint in the first place. I know this would be an isse for the spring-security project, not for this one. Please let me know if it is worth opening a thread there, or if I am not understanding something.

@jgrandja
Copy link
Collaborator

jgrandja commented Oct 12, 2022

@mpsanchis

I am confused with what you mentioned back then:

The user-info-uri is required for standard OAuth 2.0 Providers

Please review the reference on DefaultOAuth2UserService:

DefaultOAuth2UserService is an implementation of an OAuth2UserService that supports standard OAuth 2.0 Provider’s.

The DefaultOAuth2UserService is used for standard OAuth 2.0 Provider's (e.g. GitHub, Facebook) during an oauth2Login() flow. The reason it's required is because these are NOT OpenID Connect Provider's and therefore do not provide a standard UserInfo Endpoint.

CommonOAuth2Provider.GITHUB configures userInfoUri as https://api.github.com/user and scope as read:user.
CommonOAuth2Provider.FACEBOOK configures userInfoUri as https://graph.facebook.com/me?fields=id,name,email and scope as public_profile, email. Both userInfoUri are non-standard UserInfo endpoints.

This is required for standard OAuth 2.0 Provider’s because we need a way to obtain User Information in order to login the user during an oauth2Login() flow. However, with standard OpenID Connect Provider's, we could login the user using the ID_Token returned in the Token Response, making the UserInfo endpoint optional.

NOTE: the openid scope should not be configured for standard OAuth 2.0 Provider's, as this is specific to OpenID Connect Provider's only.

@mpsanchis
Copy link
Author

Thank you again, Joe. I hope this discussion is helpful for future users of Spring Security.

@uniquejava
Copy link

@sjohnr @jgrandja @mpsanchis May you shed me some light on how to call /userinfo endpoint via curl. I am even more confused.

Should I call it

  • directly via SAS server (do I need to configure SAS as resource server)
  • or call it indirectly via outh2 client (how to do that)

I read some excellent oidc primer here: https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1

and tried OIDC /userinfo endpoint online: https://okta-oidc-fun.herokuapp.com/

Now I want to apply these in SAS, I strictly followed SAS getting started guide: https://docs.spring.io/spring-authorization-server/docs/current/reference/html/

public RegisteredClientRepository registeredClientRepository() {
		RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
				.clientId("messaging-client")
				.clientSecret("{noop}secret")
				.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
				.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
				.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
				.redirectUri("http://127.0.0.1:8080/login/oauth2/code/messaging-client-oidc")
				.redirectUri("http://127.0.0.1:8080/authorized")
				.scope(OidcScopes.OPENID)
				.scope("message.read")
				.scope("message.write")
				.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
				.build();

		return new InMemoryRegisteredClientRepository(registeredClient);
	}

I noticed there are two redirect_uris configured here. I thought the first one must be used for OIDC(the name implies). And I tried different combinations. Each time I got some 401 error, but there is no hint from the SAS trace log what I did wrong.

Thank you.

The following are the script I tried.

user: `user/password`

export client_id=messaging-client
export redirect_uri=http://svc.com:8080/login/oauth2/code/messaging-client-oidc

browser http://auth.com:9090/oauth2/authorize?response_type=code&client_id=$client_id&redirect_uri=$redirect_uri&scope=openid

export code=mo7DoeekfJvhn0D8FgwkA4F0ZZZCL3jUWdlH_WcL9H4efZT4Rdn3Us8wi5EqtDZgRzWxg7nSAb5V9PmhcMQiZw5QgSjdoSdeXnjN7uNBHwoNGjqPwku094WqG_4SlSZ8

export access_token=$(curl -s -umessaging-client:secret \
-d "grant_type=authorization_code&redirect_uri=$redirect_uri&code=$code" \
http://auth.com:9090/oauth2/token | jq --raw-output '.access_token')


curl -i -H "Authorization: Bearer $access_token"  http://auth.com:9090/userinfo

@uniquejava
Copy link

Ah, I just read the userinfo endpoint document. https://docs.spring.io/spring-authorization-server/docs/current/reference/html/guides/how-to-userinfo.html#enable-user-info, and all works now. Thank you!

@brucelwl
Copy link

If OIDC is not enabled, do I need to provide my own /userinfo implementation ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

6 participants