Scopes vs Roles when accessing Resource Server with Keycloak as Authentication Server - oauth-2.0

I am using Keycloak as Authorization Server and setting up SPA (Public Client) that will be connecting to a REST service (Bearer-Only client / Resource Server).
As far as I understand, scopes are group of claims and claims are information about the subject (authenticated user). Scopes are passed at authentication step to the authorization server so that it will prompt the user to permit/deny access to those claims (user data / actions). In the case of third party client trying to access data from for example Facebook, scopes are permissions given by the user to the client to access user's data from Facebook. In case of Slack scope are used to allow / deny certain permissions for the third party client to perform operations on behalf of the user.
In my case where the resource server does not hold any user information but OAuth is used for authentication / authorization and will use the access token to get users group membership and possibly role to allow/deny access to the data on the resource server.
Correct me if I am wrong but scopes are used more for the user to allow third party client to access his/her information from OIDC provider, possibly get user information from the ID Token. On the other hand Roles / Groups that is set in the access token would be used to authorize the user to get data from the the resource server
For example here is the
Access Token:
{
"exp": 1612294865,
"iat": 1612294565,
"auth_time": 1612294524,
"jti": "e6c12a30-1b54-410a-81ba-ffe947dcebc9",
"iss": "http://localhost:8080/auth/realms/demo",
"aud": "rest-service",
"sub": "e49f2e27-4b47-492e-a714-2497d4f669f8",
"typ": "Bearer",
"azp": "js-console",
"nonce": "58faa6b8-a628-4b5a-8f0a-b33643812ef2",
"session_state": "2470b7b4-1587-43d5-8747-911b01398c3d",
"acr": "0",
"allowed-origins": [
"http://localhost:8000"
],
"resource_access": {
"rest-service": {
"roles": [
"user"
]
}
},
"scope": "openid email profile",
"email_verified": false,
"preferred_username": "test",
"group": [
"/my-group"
]
}
ID Token
{
"exp": 1612294865,
"iat": 1612294565,
"auth_time": 1612294524,
"jti": "c3242eb4-9047-4913-9ac6-be3b4e2b5745",
"iss": "http://localhost:8080/auth/realms/demo",
"aud": "js-console",
"sub": "e49f2e27-4b47-492e-a714-2497d4f669f8",
"typ": "ID",
"azp": "js-console",
"nonce": "58faa6b8-a628-4b5a-8f0a-b33643812ef2",
"session_state": "2470b7b4-1587-43d5-8747-911b01398c3d",
"at_hash": "5hOJUzWD9H2DOuYCEnc6fQ",
"acr": "0",
"email_verified": false,
"preferred_username": "test"
}
The audience(aud) for the ID token is set to the Client (js-console) and returns just some user information and really only needs openid scope. The access token aud is set to the rest service that will consume the access token and retrieve the roles and group information.
This is a different usage of OAuth which in this case the user doesn't need to consent to anything but will authenticate and use the access token to get information based on the group membership.
Is my understanding is correct?
Thanks!

Related

Azure AD OAuth Token Claim - Populate DisplayName attribute for Application Users

When I request an OAuth token from Azure AD for an application user (using a client_id and client_secret) targeting the correct resource audience (target application), I don't know how to get Azure AD to populate a claim for the client's DisplayName attribute (primarily for an application client, but also users should work too) in Azure AD.
POST /<tenant_id>/oauth2/token HTTP/1.1
Host: login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials&client_id=<client_id>&resource=<target_aud_app_uri>&client_secret=<client_secret>
{
"typ": "JWT",
"alg": "RS256",
"x5t": "xxx",
"kid": "xxx"
}.{
"aud": "<target_aud_app_uri>",
"iss": "https://sts.windows.net/<tenant_id>/",
"iat": 1619676176,
"nbf": 1619676176,
"exp": 1619680076,
"aio": "xxx",
"appid": "<client_id>",
"appidacr": "1",
"idp": "https://sts.windows.net/<tenant_id>/",
"idtyp": "app",
"oid": "xxx",
"rh": "xxx",
"roles": [
"XXX"
],
"sid": "xxx",
"sub": "xxx",
"tenant_ctry": "AU",
"tenant_region_scope": "OC",
"tid": "xxx",
"uti": "xxx",
"ver": "1.0"
}.[Signature]
In the target application configuration in Azure AD, I've added all the optional claims it allows in the UI, and even tried adding 'display_name' via the manifest but it doesn't know how to pick it up. Is there a way to do this?
This question has been asked before.
This is possible under the Microosft.graph namespace only cannot be used for any custom app. In other words, only the ms graph api token has app_displayname claim. For the token of the application custom api, it is currently not possible to add the app_displayname claim, at least for now this is impossible.
I suggest you submit user voice, and I will vote for it.

WSO2 APIM - Add user roles in JWT payload

I'm developing some SpringBoot microservices that exposes REST through WSO2 APIM.
Microservice itself does not implement any kind of authentication or authorization mecanism, it is delegated to APIM.
If I set API to use Password Grant as described here, front end application can authenticate and generate JWT token.
The problem now is that I can't fetch user roles from JWT payload because it is not being added by APIM. This information is important because front-end render menus and buttons based on user roles.
The user I'm passing when generate token does have some roles as you can see bellow:
But generated JWT token does not include any information about roles. Here is a sample token:
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5UZG1aak00WkRrM05qWTBZemM1TW1abU9EZ3dNVEUzTVdZd05ERTVNV1JsWkRnNE56YzRaQT09In0.eyJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC9hcHBsaWNhdGlvbnRpZXIiOiJVbmxpbWl0ZWQiLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC92ZXJzaW9uIjoidjEiLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC9rZXl0eXBlIjoiUFJPRFVDVElPTiIsImlzcyI6IndzbzIub3JnXC9wcm9kdWN0c1wvYW0iLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC9hcHBsaWNhdGlvbm5hbWUiOiJDYWRhc3RybyBkZSBDbGllbnRlcyIsImtleXR5cGUiOiJTQU5EQk9YIiwiaHR0cDpcL1wvd3NvMi5vcmdcL2NsYWltc1wvZW5kdXNlciI6ImVtaWxpb0BjYXJib24uc3VwZXIiLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC9lbmR1c2VyVGVuYW50SWQiOiItMTIzNCIsImh0dHA6XC9cL3dzbzIub3JnXC9jbGFpbXNcL3N1YnNjcmliZXIiOiJhZG1pbiIsImh0dHA6XC9cL3dzbzIub3JnXC9jbGFpbXNcL3RpZXIiOiJVbmxpbWl0ZWQiLCJzY29wZSI6ImRlZmF1bHQiLCJleHAiOiIxNTk5NTYyOTQ4MDI4IiwiaHR0cDpcL1wvd3NvMi5vcmdcL2NsYWltc1wvYXBwbGljYXRpb25pZCI6IjIiLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC91c2VydHlwZSI6IkFwcGxpY2F0aW9uX1VzZXIiLCJjb25zdW1lcktleSI6IktJaTdnUk1RYmg1OWZGbmpVOFhNbnhGcm9pNGEiLCJodHRwOlwvXC93c28yLm9yZ1wvY2xhaW1zXC9hcGljb250ZXh0IjoiXC9ia25nXC92MSJ9.km4w2V7dGmoGl8f4_ZqKHvdofAPLOOw__GPjWKrpjYelbi7IjDIpRODEZNn8hE1krRdDTSjKRviJ-NBvXtTXIiLdfPh1p-zNtX26vrS77ZcSZ2WsQA7Ku21YMqcm6cyZvEhZ99qfTxOtbJfkwt6Yt8itkyr-aqk83pNp85LTnwtNboib9VOOvh37zNEJUImzKw4WvENp4SGLuHO978FriHyHPN9vibzPjpItW5DOXTFNdN4rP6RK_vcOH6hpuZHwivJpTHxf9qMB3Gd2yTig-Hkr-sZGbx89pQf8kqtCLWbhRG5jOtcEJNf2CSNLB0Glg_e4F6LfhVD5JUCz15jdlg
When I extract it in https://jwt.io/ I get following payload:
{
"http://wso2.org/claims/applicationtier": "Unlimited",
"http://wso2.org/claims/version": "v1",
"http://wso2.org/claims/keytype": "PRODUCTION",
"iss": "wso2.org/products/am",
"http://wso2.org/claims/applicationname": "Cadastro de Clientes",
"keytype": "SANDBOX",
"http://wso2.org/claims/enduser": "emilio#carbon.super",
"http://wso2.org/claims/enduserTenantId": "-1234",
"http://wso2.org/claims/subscriber": "admin",
"http://wso2.org/claims/tier": "Unlimited",
"scope": "default",
"exp": "1599562948028",
"http://wso2.org/claims/applicationid": "2",
"http://wso2.org/claims/usertype": "Application_User",
"consumerKey": "KIi7gRMQbh59fFnjU8XMnxFroi4a",
"http://wso2.org/claims/apicontext": "/bkng/v1"
}
How do I add user roles to JWT payload? Do I need to implement a custom generator as described here?
Thanks in advance!
Easiest way to get role claim included in the auth JWT is to add a claim mapping in service provider level and request the token with openid scopes. To do this try below steps.
Log in to management console https://<host>:<port>/carbon
List service providers in the left menu
Go to edit on the required service provider (Each application in the developer portal has a mapping service provider)
Add a claim mapping to role claim as below
Send the token request with the scope=openid parameter
curl -k -X POST https://localhost:8243/token -d "grant_type=password&username=<Username>&password=<Password>&scope=openid" -H "Authorization: Basic <Credentials>"
Response access token will contain roles in this format
{
"sub": "admin#carbon.super",
"iss": "https://localhost:9443/oauth2/token",
"groups": [
"Internal/subscriber",
"Internal/creator",
"Application/apim_devportal",
"Application/admin_NewApp_PRODUCTION",
"Internal/publisher",
"Internal/everyone",
"Internal/analytics",
],
...
}

Access Token contains almost all information from Identity Token

The tokens below are returned upon initial token request with Azure AD B2C. access_token contains most information that id_token contains.
My questions are that:
1 Since access_token contains most information that id_token contains, what scenario can access_token be used in place of id_token? what scenario must id_token be used instead of access_token?
2 What are the rules regarding use of both access_token and id_token in general?
Result upon initial token request
{
"access_token": "access_token",
"token_type": "Bearer",
"expires_in": "3600",
"refresh_token": "refresh_token",
"id_token": "id_token"
}
Below is the decoded token for the respective token:
Please note that access_token contains most information from id_token, including user information.
access_token
{
"iss": "url",
"exp": 1550539339,
"nbf": 1550535739,
"aud": "audience ",
"idp": "LocalAccount",
"sub": "guid",
"name": "user#email.com",
"emails": [
"user#email.com"
],
"tfp": "B2C_1_ROPC_Auth",
"ver": "1.0",
"iat": 1550535739,
"azp": "guid"
}
id_token
{
"iss": "url",
"exp": 1550539339,
"nbf": 1550535739,
"aud": "audience ",
"idp": "LocalAccount",
"sub": "guid",
"name": "user#email.com",
"emails": [
"user#email.com"
],
"tfp": "B2C_1_ROPC_Auth",
"ver": "1.0",
"iat": 1550535739,
"auth_time": 1550535739,
"at_hash": "access_token hash"
}
Update
Further to question 1 above:
Since access_token contains most information that id_token contains, can access_token be used instead of id_token? That is, when id_token must be used instead of access_token?
I will take google as a example,
1) let say you want to implement social login where u mostly need fields like email, 'first_name', 'last_name'., these fields are mostly available in id_token. so id_token is perfect suitable for authentication.
2) later u need to fetch google contacts of the login user., In such scenarios u can't get contacts of the user in id_token. this is where access_token comes, though u won't have the contacts in access_token but all permission to fetch the contacts.
An ID token is intended for authentication. It is represented as a JSON Web Token (JWT) that contains claims about an authentication event including the identity information for a resource owner. The ID token is intended for a client application (a single-page app) and its aud (audience) claim contains the OAuth 2.0 client_id of the client application.
An access token is intended for access by clients to resources that are owned by a resource owner. It is represented as either a JWT (e.g. Azure AD B2C represents an access token as a JWT) or an opaque token. The access token is intended for a resource application (e.g. an API app) and its aud claim contains the OAuth 2.0 client_id of the resource application.

Refreshing tokens with GIDSignIn iOS

I am currently using google sign to authenticate users to my app, but am having problems with the id_token after it's been refreshed. I have the following variables set up. One is for the iOS client and one is for the server. This is following AWS examples. I am using AWS to access the server and therefore need to have a client id and web application id.
static let GOOGLE_CLIENT_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx7dde.apps.googleusercontent.com"
// Backend web application client ID
static let GOOGLE_WEB_APPLICATION_ID = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx65ap.apps.googleusercontent.com"
When I first log in the application it works as expected. I get an id token back that has the following:
{
"iss": "https://accounts.google.com",
"at_h ash": "xxxxxxxxxxxxxxxxxxxxxx",
"aud": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx65ap.apps.googleusercontent.com",
"sub": "xxxxxxxxxxxxxxxxxxxxxx",
"email_verified": true,
"azp": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx7dde.apps.googleusercontent.com",
"email": "2222MyNewEmailIsHere#gmail.com",
"iat": 1475747692,
"exp": 1475751292
}
You can see that the "aud" matches the GOOGLE_WEB_APPLICATION_ID and the "azp" matches GOOGLE_CLIENT_ID.
However if I do a refreshTokensWithHandler like so:
GIDSignIn.sharedInstance().currentUser.authentication.refreshTokensWithHandler { (GIDAuthentication, error) in
self.googleAuth = GIDAuthentication;
self.completeGoogleLogin()
}
I get a problem with the response. The id token is being refreshed correctly and thus have a new id token. However the "aud" seems to be overwritten with the client id. This happens whether the token has expired or not.
{
"iss": "https://accounts.google.com",
"at_h ash": "xxxxxxxxxxxxxxxxxxxxxx",
"aud": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx7dde.apps.googleusercontent.com"",
"sub": "xxxxxxxxxxxxxxxxxxxxxx",
"email_verified": true,
"azp": "xxxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxx7dde.apps.googleusercontent.com",
"email": "2222MyNewEmailIsHere#gmail.com",
"iat": 1475747692,
"exp": 1475751292
}
As you can see, the "aud" and the "azp" is the same in the second response. When I then go to make a request to the server it's complaining that the token can't be verified.
I am wondering if anyone else has experienced this? It seems like an issue with GIDSignIn maybe.

Not getting all profile informations when verifying a google token authenticity

I'm using omniauth on client-side to connect with a google account. Everything is working well, I'm able to get the id_token and some more informations such as the email, name, family_name, given_name etc...
Here is how I configured the provider in app/config/initializer/omniauth.rb :
provider :google_oauth2, ENV['GOOGLE_ID'], ENV['GOOGLE_SECRET'], scope: "profile,email"
Then, as a test, to verify the authenticity of the id_token, using Postman I send a POST request such as:
https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=x
replacing x by the id_token I got on my client
I get a valid answer such as :
{
"iss": "accounts.google.com",
"at_hash": "xxx-xxx",
"aud": "xxx-xxx.apps.googleusercontent.com",
"sub": "xxx",
"email_verified": "true",
"azp": "xxx-xxx.apps.googleusercontent.com",
"email": "xxx#gmail.com",
"iat": "xxx",
"exp": "xxx",
"alg": "xxx",
"kid": "xxx"
}
When I connected with my client, I allowed the application to access my profile informations and my email, and I've been following this tuto:
https://developers.google.com/identity/sign-in/web/backend-auth
who says :
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser#gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
I can't figure out why I'm unable to get the 5 last fields
I'm working on an API and I'd like my API to be able to fill a User model by itself, only by getting the id_token.
I'm able to get those informations on the client-side but I think this is not the client job to send this extra informations to my API right?
Token calls don't include profile info... for that you muse use:
https://www.googleapis.com/plus/v1/people/me/openIdConnect?access_token=YOUR_ACCESS_TOKEN
Since you are doing this with ruby there are 2 things i can suggest trying
Try adding your scopes as an array ["profile", "email"]
Add an extra parameter fetch_basic_profile from here

Resources