AAD groups claim missing in JWT token for some users - oauth-2.0

I'm experiencing some strange behavior on our AAD. After a user signed in successful, we're getting an unauthorized for some users on our API calls. Turns out that a claim in the JWT is missing. Some users are getting the "groups" claim (array of all groupIds he belongs to) and some are getting the "hasgroups" claim (a boolean if the user has groups, no Ids). As our API app is checking this "groups" claim for authorization, the users who don't have this "groups" claim are getting a 403.‬
‪Nevertheless, in the manifest of the app registration I set the “groupMembershipClaims” from “null” to "All" or "SecurityGroup", which should do both the trick. Also set the "oauth2AllowImplicitFlow" to true as we're working with an Angular app which uses OAuth2. Next to that I've compared almost all users settings and apart from some extra groups the users are identical.‬ The affected users don't have a lot of groups, some have even around the 5 groups at max.
Do I overlook something or what's causing this difference in claims? How can I solve this so all users are getting the "groups" claim?

Got this feedback from MSFT internals:
In the implicit flow, oauth will return the Jwt directly from the
intial /authorize call through a query string param. The http spec
limits the length of a query string / url, so if AAD detects that the
resulting URI would be exceeding this length, they replace the groups
with the hasGroups claim.
And this
This is by design when using implicit grant flow, regardless the
"groupMembershipClaims" setting in the manifest. It's to avoid to go
over the URL length limit of the browser as the token is returned as a
URI fragment. So, more or less after 4 user's groups membership,
you'll get "hasgroups:true" in the token. What you can do is to make a
separate call to the Graph API to query for the user's group
membership.
So will need to do an extra roundtrip to Graph API in order to get the user groups. Hope this helps others too.

This is now documented in the Azure AD token reference at https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-token-and-claims.
For the OAuth2 implicit grant flow it uses the hasGroups token and the documentation states for this token:
Used in place of the groups claim for JWTs in implicit grant flows if the full groups claim would extend the URI fragment beyond the URL length limits (currently 6 or more groups).
For other flows:
if the number of groups the user is in goes over a limit (150 for SAML, 200 for JWT) then an overage claim will be added the claim sources pointing at the Graph endpoint containing the list of groups for the user.
You can use the Graph API to obtain a user's groups using https://graph.windows.net/{tenantID}/users/{userID}/getMemberObjects.
Alternatively there is the endpoint at https://graph.windows.net/myorganization/isMemberOf?api-version as documented at https://msdn.microsoft.com/library/azure/ad/graph/api/functions-and-actions#isMemberOf

Related

How to configure Azure AD Oauth2 to return all user groups on login

I have an AD registered application which has an integration with Azure AD for SSO. It uses the Oauth2 strategy, by using the omniauth-azure-activedirectory-v2 gem.
I want to map a users security groups to my applications authorization model and for this I need the names of the security groups.
I want to reliably get a users security groups and the group names on login and I'm not able to. I get them sometimes correctly, sometimes in a uuid format and sometimes not at all.
I have an optional group claim set up for my application in Token Configuration and configured to return sam_account_name for all attached groups.
This seems to work fine for some clients, the groups are returned as for example "Admin_APP", but for others I seem to have the following issues:
A Users groups are returned but only as a ID(c5bb3738-59f1-4718-b34c-2dfac761e023), even tough I requested the name.
A User has "readable groups" but not all assigned in AD, some are missing.
Is this a configuration on my application side or should the organization adding my application to their AD configure their groups or my application? Or should I not rely on the token cliam at all and fetch the groups using the GraphQL API Azure offers?
I noticed when adding the application myself I need to give permissions for my user.profile but it doesn't show allowable permissions for groups. Also in the Enterprise application tab for the organization under permissions I can't seem to find the group claim I added. Only openid, profile and email.
I tried to reproduce the same in my environment and got the results like below:
I configured the Optional claims in Azure AD Application:
I generated the access token via Postman by using below parameters:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:user.read openid
grant_type:authorization_code
redirect_uri:redirectUri
code:code
When I decoded the token, I got the Group IDs instead of Group Name like below:
Note that: If you are configuring sAMAccountName as the claim value in the token, then it only returns the Group which is synced from on-premises AD. By default, Group ObjectID is returned in the group claim value.
By default, groups emitted in a token is limited to 150 for SAML
assertions and 200 for JWT.
I agree with junnas, you can make use of Graph API to get the user groups like below:
https://graph.microsoft.com/v1.0/users/UserID/memberOf
To get the only list of security groups user belongs to, you can make use of below query:
https://graph.microsoft.com/v1.0/users/UserID/memberOf?Filter("mailEnabled eq false and securityEnabled eq true")
References:
List a user's direct memberships - Microsoft Graph v1.0 | Microsoft Learn
How to get groups to appear as claims in the access_token by AmanpreetSingh-MSFT

Extending OAuth2 MS AD access_token data

I am missing some understanding of OAuth2 access_token hope someone can explain or guide me to what I am missing.
I am using Microsoft Azure AD as an authentication provider for my application, I used the returned id_token after successful authentication to extend it with some additional data custom to my application (to facilitate authorization).
I am doing this throw JWT.sign, I decode the data from id_token and add data then I sign it using a secret key saved at the server.
My question is, can I do the same for access_token? When I tried to do so, I get unauthorized.
Am I doing something wrong? Or this is not possible? And why is this happening, I don't find any request made to MS to validated my new signed access_token.
You should never change tokens issued - this is not a correct thing to do. But your point about using domain specific claims is totally valid - all real world systems need these for their authorization.
OPTION 1
Some specialist providers can reach out at time of token issuance and contact your APIs, to get domain specific data to include in tokens. See this Curity article for how that works. I don't think Azure AD supports this though.
PRIVACY
It is best to avoid revealing sensitive data in readable tokens returned to internet clients. If you include name, email etc in ID tokens or access tokens this may be flagged up in PEN tests, since it is Personally Identifiable Information and revealing it can conflict with regulations such as GDPR.
Curity recommends protecting access tokens by issuing them in an opaque reference token format - via the phantom token pattern.
OPTION 2
An option that would work fir Azure AD is to adopt the following approaches:
Look up extra domain specific claims in your API when an access token is first received, then cache results for further API requests with the same access token. See this Azure AD Code Sample class of mine for some code that builds a custom ClaimsPrincipal. Note that the API continues to validate the JWT on every request.
If the UI needs extra domain specific claims then serve them from your API, which can return both OAuth User Info and domain specific data from its ClaimsPrincipal to the UI. See this API controller class for how that looks. Personally I always do this and never read ID tokens in UIs - which should also never read access tokens.
Applications interacting with Azure AD, receive ID tokens after authenticating the users. The applications use access tokens and refresh tokens while interacting with APIs.
The id_token is a JSON Web Token (JWT) which has user profile
attributes in the form of claims. The ID Token is consumed by the
application and used to get user information like the user's name,
email.
The Access Token on the otherhand is a credential that can be
used by an application to access an API.
So if you need application to access api, there the access token is used and you may follow the suggestion steps provided by Tiny Wang
Similar to id tokens, access tokens are also signed, but they are not
encrypted. As per IETF OAuth (RFC 6749) standard specification ,
access token can have different formats and structures for each
services whereas, id token should be JWT format.
To validate an id_token or an access_token, your app has to validate
both the token's signature and the claims. To validate access tokens,
your app should also validate the issuer, the audience, and the
signing tokens.
So in production application, you should get id token by specifying
“id_token+code” or “id_token+token” as response_type to verify
whether the authentication is correctly succeeded. It means it uses
the id_token for authentication and “code” to exchange access_token
to access the resource for authorization.
In short id_token is used to identify the authenticated user, and the
access token is used to prove access rights to protected resources.
Refer this for the information regarding access token and id token.

Getting Security Groups in JWT Access Token

I would like a JWT access_token to contain a list of security group. From reading the documentation I attempted setting "groupMembershipClaims": "All", in the manifest of my application regostraton.
The setup is that I have a azure ad domain with 2 users. Then I have 2 groups: [user, admin] Each user has one of the groups assigned to it. Each group is of type security.
I am attempting to perform the OAuth Code flow manually, by going to the url formated similar to this:
https://login.microsoftonline.com/<tenantid>/oauth2/v2.0/authorize?client_id=<application-id>&response_type=code&redirect_uri=http://localhost:8080&response_mode=query&scope=offline_access%20user.read%20mail.read&state=12345
Then I login using the user account I setup in azure ad. Then I use postman to submit a post request to https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token with parameters:
client_id = <client-id>
code = code from previous login,
redirect_uri = http://localhost:8080
grant_type = authorization_code
scope = user.read mail.read
I get a valid response back, with an access_token. However when I decode it there is no group claim listed at all.
How can I get the groups listed in the token?
According to my research, Azure AD v2 endpoint of Azure Active Directory does not yet support groups claims in its token. For more details, please refer to the document. If you want to get all groups one user belongs to, you'll need query the groups from Microsoft Graph API. You can find the API documentation here:https://learn.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0.
If you use the app to access own API, in order to get group claim in the access_token, you need to configure the groupMembershipClaims value as you want in the API manifest, then you could get the group claim information in the access_token.
But if the access resource is MS graph API or Azure AD graph API, you could not configure for them, you could only use the openid connect to get the id_token, and then you could see the group claim in the id_token. In your flow, you could add openid in the scope, then you could find the group information in the id_token.
I tried oauth2 code flow in the postman:
And it returns the access_token and id_token. To parse the id_token, I get the group information:

The 'sub' claim value is different between Access and Id tokens

I am using the Resource Owner Password grant flow and requesting id token as well (the scope includes openid). I am sending the following to the endpoint:
client_id
client_secret
grant_type=password
username
password
scope
In the response I get the access token and the id token. The value for the sub claim is different between the two tokens. Why is this the case?
Update
It seems that the user id is actually an oid claim. This is described in Azure AD ID token reference.
Text describing the oid claim:
The immutable identifier for an object in the Microsoft identity system, in this case, a user account. This ID uniquely identifies the user across applications - two different applications signing in the same user will receive the same value in the oid claim. The Microsoft Graph will return this ID as the id property for a given user account. Because the oid allows multiple apps to correlate users, the profile scope is required in order to receive this claim. Note that if a single user exists in multiple tenants, the user will contain a different object ID in each tenant - they are considered different accounts, even though the user logs into each account with the same credentials.
Text describing the sub claim:
The principal about which the token asserts information, such as the user of an app. This value is immutable and cannot be reassigned or reused. The subject is a pairwise identifier - it is unique to a particular application ID. Therefore, if a single user signs into two different apps using two different client IDs, those apps will receive two different values for the subject claim. This may or may not be desired depending on your architecture and privacy requirements.
However, I am still not clear why the sub claim is different between the access and id tokens.
The subject (sub) claim is unique for the user and the service for which the token is intended (identified by the audience (aud) claim).
Usually, the ID Token and Access Token audiences will be different: the ID Token audience is the client app where the user is signing in, and the Access Token audience is the resource server the client app will attempt to access (on behalf of the signed-in user).
The Resource Owner Password Credentials grant type is not supposed to be used for user authentication as per the OpenID specification, only flow with that interact with the user through the authorization endpoint are allowed to do so (i.e. Implicit, Authorization Code, Hybrid and None grant type flows at the time of writing).
If you receive an ID token using the ROPC flow, then the Identity Provider proposes specific means that are out of scope of the OpenID Connect spec and may have specific and non-standard features that should be documented.
In any case, on Client side you should only rely on the sub claim in the ID Token. The Client is not supposed to parse the access token as it is only meant to be used by the resource server.
The sub claim depends on the IdP policy and the client configuration. The sub claim may be unique to the application (or application group). Please refer to Subject Identifier Types section of the specification.

How are access tokens distinguished between resource providers in OpenID Connect?

I am working on creating an OpenID Connect (OIDC) Provider based around django-oidc-provider. I have been reading up on the OpenID Connect Spec, and I cannot figure out how access tokens are unique for a certain application.
Consider the following example with a user, Bob:
Bob wants to login to application A, so he goes to its interface and is redirected to the OIDC Provider. After authentication he is redirected (implicit flow) back to Application A with an ID token and an access token. He then makes a request at "/image/1" to A's API with his access token. The API uses the access token to reach out to the OIDC Provider to assert the user's identity as Bob. The API then returns the data at "/image/1" for user Bob, assuming that info exists. Bob continues to send his access token to A's API for any subsequent requests.
Then, Bob decides he wants to access application B's API. He sends B's API the same access token that he used with A's API. B's API reaches out to the OIDC Provider with the token and asserts the user's identity as Bob. B's API then returns the requested info for Bob.
What prevents this from happening? I see at least two possible solutions to this:
When reaching out to Google's token validation endpoint the "aud" parameter is returned. Application B's API would have to check this parameter to decide that the token is not valid for it's own API?
An additional scope must be added when requesting the token that is specific to the resource provider say "app-A-api". Then when an API is validating a token, the API would ensure the token contains the needed scope.
Which of these methods, or others, are in line with the OIDC spec?
If one of the above should be used, am I correct in assuming I should add a new /tokeninfo endpoint that returns the scope or aud, rather than add that info to the info returned at the /userinfo endpoint?
Any input is appreciated. I think a lot of my confusion comes from not seeing the "scope" param being used to delegate access to a resource provider in any OIDC examples.
I think the thing you are missing is that the application A and its API are two separate applications. So the tokens are issued for the application A. If the app-A-api uses the access token just for the user authentication, it's better to use an ID token - it can be validated without accessing the OAuth2 server. In this scenario, the app-A-api manages its user permissions by itself.
If the app-A-api needs the token to get a list of scopes (permissions) of its client, then use the access token. But in this scenario, the app-A-api (and app-B-api) are just accepting the access token - they are not the target audience (aud attribute) of the token. The application A is the audience of the tokens.
The APIs just check whether the access token contains scopes relevant for them. They trust the token issuer and it's up to the users to decide whether they trust the application A to perform actions on their behalf.
As an example, if a JavaScript application C (app-C) uses just Google Drive and Google Plus for its actions, then app-C will ask its user for an access token with scopes belonging to Google Drive and Google Plus. It will be just one token and both Google APIs will accept it.
And about the tokeninfo endpoint, it has it's own RFC called OAuth 2.0 Token Introspection, so you can check it.

Resources