I am going through this api doc (source) of Spring OAuth2 ClientDetails Interface. And I am not sure how this i.e clientDetails.getAuthorities() works or helps in the authorization flow. The API docs describes it as below:
Collection<org.springframework.security.core.GrantedAuthority> getAuthorities()
Returns the authorities that are granted to the OAuth client. Cannot return null. Note that these are NOT the authorities that are granted to the user with an authorized access token. Instead, these authorities are inherent to the client itself.
Returns:
the authorities (never null)
And I understand the above is different from the userDetails.getAuthorities() of UserDetails Interface. The API docs says as below:
java.util.Collection<? extends GrantedAuthority> getAuthorities()
Returns the authorities granted to the user. Cannot return null.
Returns:
the authorities, sorted by natural key (never null)
I understand that, in ClientDetails we are dealing with granting permission to the OAuth2 Client, while in UserDetails we are granting the permission to User i.e Principal.
But how is the former different from later and when to use the former.
A little elaboration -
When dealing with OAuth, we are dealing with tokens issued by Auth Server. The tokens are typically jwt tokens with information about the authorities granted to the User. A typical base64 decoded token's payload might look like below:
{
"app-userId": "c54a-4140-9fa0-0f39",
"user_name": "abc#xyz.com",
"scope": [
"all"
],
"exp": 1656929583,
"authorities": [
"app1_viewer",
"app1_modifier",
"app2_viewer",
"app2_blog_creator],
"client_id": "client_A"
...
}
The authorities above are the authorities of the User who is logged-in and using the app.
When the user accesses app1 - it checks the above jwt token to make sure the user has the required permissions/authorities.
If app1 has to call app2, then app1 passes the above jwt which is specific to current user (as Authorization header) to app2. App2 processes this jwt token received in headers and checks if the user has permissions to access/edit the resource. And returns 200 or 403 accordingly.
As you can see, the process is dealing with userDetails.getAuthorities(). So, how and where or in which case does clientDetails.getAuthorities() help. What are the use cases/examples of it.
Could some one explain. Thanks for any answers.
I am not a Java hero, but I work a lot with Azure and Oauth2. Your question got me thinking what that could be. From what I learned is that Authorities decide who gets a valid token and who doesn't (token issuer). From the Microsoft Azure documentation about MSAL I got the following:
Authority
The authority is a URL that indicates a directory that MSAL can
request tokens from.
Common authorities are:
https://login.microsoftonline.com/TENANT/
Sign in users of a
specific organization only. The in the URL is the tenant ID
of the Azure Active Directory (Azure AD) tenant (a GUID), or its
tenant domain.
https://login.microsoftonline.com/common/ Sign in users
with work and school accounts or personal Microsoft accounts.
https://login.microsoftonline.com/organizations/ Sign in users with
work and school accounts.
https://login.microsoftonline.com/consumers/ Sign in users with
personal Microsoft accounts (MSA) only.
A valid use case could be that one wants to build a multi-tenant application. So users from multiple authorities are able to sign-up and sign-in to your application. On basis of the authority you can filter which data a user can see, only data from the users' organization for example. Basically the same idea as how you would use the application roles from userDetails.getAuthorities() to restrict what a user can see or do according to his role within that specific application.
Related
Using Gitlab provided access tokens for the OpenID scope, i.e.
https://gitlab.com/oauth/userinfo?access_token=<bearer token>
returns a limited amount of information e.g. username, groups, etc. However, it does not return the user's email address.
The Gitlab documentation indicates that:
The claims sub, sub_legacy, email and email_verified are included in
the ID token, all other claims are available from the /oauth/userinfo
endpoint used by OIDC clients.
Given the AccessToken - how do I retrieve the ID token?
*This is a known / discussed issue cf. here
It all depends on what scopes you ask for when you first send the initial authentication request to GitLab.
You need to ask for the email scope to get that information back and you should get the ID-token back at the same time as you get your first access-token.
When you obtain code through authorization request, you need to specify the scope, and the scope needs to include openid,read_user,profile,email.
When exchanging the token with the code obtained in the first step, you can return to access_token and id_token.
When the access_token obtained in the second step is used to obtain the user information, it can return the user's e-mail.
Refer to the explanation of the scope used above:
openid: Grants permission to authenticate with GitLab using OpenID Connect. Also gives read-only access to the user's profile and group memberships.
read_user: Grants read-only access to the authenticated user's profile through the /user API endpoint, which includes username, public email, and full name. Also grants access to read-only API endpoints under /users.
profile: Grants read-only access to the user's profile data using OpenID Connect.
email: Grants read-only access to the user's primary email address using OpenID Connect.
With an OAuth2 implementation (either developed in-house, or a 3rd party like Google, Facebook, Login With Amazon, etc.), is it possible to generate an authentication code on behalf of a user logged into a mobile app or web app without requiring any action from the user?
The typical flow to obtain the authentication code requires the user to authenticate and authorize the requested scope. But in this case, the user is already authenticated into the app, so I want to avoid requiring the user to log in again.
The authentication code is required for invoking an external third-party API that will eventually exchange the authentication code for refresh/access tokens. The backend system (associated with the API) needs to get its own refresh/access token based on the authentication code shared with it. This is not for a one-time use of the token; the system needs to have its own tokens for that logged in user, independent of the mobile client.
It is possible to get user token for another client. You do not need new authentication code, you just call token endpoind with some params. For example, in Keycloak this flow is called Token Exchange. You need to configure clients in the Keycloak and then you can call token endpoint with access token you have.
{
client_id: your client id,
client_secret: your client secret,
subject_token: token you have
audience: target client id,
grant_type: urn:ietf:params:oauth:grant-type:token-exchange,
requested token type: urn:ietf:params:oauth:token-type:refresh_token
}
You can read about this flow here:
https://tools.ietf.org/id/draft-ietf-oauth-token-exchange-12.html
It is also called On-Behalf-Of flow like in Azure:
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
I have:
Spring boot client application with some public endpoints and private endpoints which require #PreAuthorize("#oauth2.hasScope('resource.read')") for example
I have a external authorization server: Cloudfoundry UAA
I have a external OIDC provider linked to UAA I can use that to authenticate a person, I receive a Person_ID from the ID_Token from that external OIDC provider
Now I need to change UAA core code to implement my logic of using that Person_ID and searching for equivalent user from LDAP which shares the same Person_ID and then I will need to add it's usergroups to the token for the client. (I have done it currently in the /userinfo endpoint)
So I have done this logic in the /userinfo endpoint, when client receives a access token (From client, redirected to UAA, from UAA to OIDC for AUTH, then back again for the token and then this token is sent to client, now client can take the token and ask for the /userinfo which will then have it's user roles)
Is this bad logic? Should I add the LDAP implementation(step4) inside the access token already somehow?
Really, as is often the case with design questions, it depends.
The key to remember is that OIDC and its associated id_token are for authentication. It's common for the /userinfo response to state claims about who the user is. Part of the user's identity might be their role.
OAuth and its associated access_token, on the other hand, are for authorization. It's common for the access token to state claims about what the client is authorized to do. What a client might be able to do may be different than the user's role.
Think about what decisions this client will need to make. It may be able to make choices like which of its pages it can show, based on the roles that it inferred from the /userinfo response.
Think about what this client will communicate with. Maybe it will communicate with a resource server. If the client passes the access_token obtained during login, then that token should indicate what the client is authorized to do.
I am asking a question conceptually here as I am trying to understand the relationship between scopes and user roles in an OAuth2 based system.
As I am implementing an API, I want to restrict access to specific resources by using scopes on the resources. I understand the use of access tokens to request resources, and I believe my understanding to be correct in that you specify your scope(s) when requesting the access token.
What I am not entirely sure of is how restriction of scopes would work based on specific roles that an authenticated user is in. Let's assume Bob is an admin and Sue is a regular user. We have some resources protected by an is_admin scope. What stops Sue from requesting (and receiving) is_admin scope in her access token?
I am thinking that what should happen is the following:
Bob authenticates.
Bob's roles are looked up after his authentication is complete. His "admin" role has the "is_admin" scope attached.
Bob asks for an access token with all the scopes collected from his various roles
Bob is automatically given those scopes for his access token
Is it up to my calling app to enforce only sending asking for the scope Bobs needs? Or is there something I am missing with regards to scopes?
Can someone please enlighten me with some simple examples?
In OAuth2, there are the following roles:
Resource owner - usually some person
Auth provider - the OAuth2 server
Resource server - an API that requires an access token and validates its scopes
Client application - application requesting an access token with some scopes.
To understand OAuth2, it's necessary to think about it as a protocol for access rights delegation from a Resource owner to a Client application. So the main use case is: the Client application wants to access the Resource server. In order to do that, the Client application needs an access token issued by the Auth provider and authorized by the Resource owner (which gets authenticated by the Auth provider).
In your description, the Client application is missing. Let's assume it's a frontend application for your API. It needs an access token with scopes admin-user-scope or regular-user-scope. So it redirect a user (Resource owner) to the Auth provider, requesting both scopes.
The Auth provider authenticates the user and asks him/her for a consent on granting some of the requested scopes to the Client application. The Auth provider may remove some scopes - for example the admin-user-scope for non-admins. The Auth provider may give the user a possibility to remove some scopes too.
The Client application receives an access token (or a grant) with scopes in a redirect URI. If the granted scopes differ from the requested scopes, the Auth provider sends a list of granted scopes (the scope URL parameter) along with the access token, so the Client application knows what actions it can perform with the access token.
Then the client application may access the Resource server and the Resource server makes sure that the provided access token contains required scopes. The Resource server uses the OAuth2 introspection endpoint to validate the token and to get a list of its scopes.
This example by Microsoft describes implementing an OAuth 2.0 Authorization server. I'm implementing the Authorization Code Grant flow. In the downloaded sample code, the /authorize endpoint asks the user every time to grant permission when logging in. As users want to grant permission only once at the first time login, should I persist it myself for each user or has OAuth support for this by default?
What's the best practice in this scenario?
Thanks in advance.
The OAuth 2.0 specification itself does not mention anything about the feature. Therefore, if the implementation of the authorization server you are using does not have the feature, you yourself have to implement it.
To achieve the feature, you need to store information about "who (user) has granted what permissions (scopes) to whom (client application)" for each combination of user and client application. In addition, probably you will want to keep the information even after all access tokens issued to each combination have expired in order to avoid asking the user again.
If I were you, I would add an internal API to the authorization server. The API would receive a user ID and a client ID and return a list of scopes which have been granted by the user to the client application in the past. If there were such an API, you would be able to use it when you generate an authorization page.
FYI:
"Granted Scopes API" of Authlete is an example. /api/client/granted_scopes/get API accepts subject and clientId request parameters and returns a JSON like below.
{
"serviceApiKey" : <Service API Key>,
"clientId" : <Client ID>,
"subject" : <User's Unique ID>,
"latestGrantedScopes" : <Scopes granted by the last authorization process>,
"mergedGrantedScopes" : <All the scopes granted so far>,
}
/api/client/granted_scopes/delete API accepts subject and clientId request parameters and deletes the remembered record if any.
Note that Granted Scopes API works only on dedicated Authlete servers. It does not work on the shared Authlete server (api.authlete.com). It's because garbage records may accumulate if API callers don't call /api/client/granted_scopes/delete API as necessary, and such garbage records waste the shared storage.