Are the MSAL claims constants available in a package somewhere? - azure-ad-b2c

I've moved my authentication to MSAL and I want to examine my claims on the client side to see, for example, the object ID that I have in Azure B2C (and thus, give my client an identity).
On the server, the objectId claim appears to be returned as:
{http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier: 63fd3d89-26ff-4934-907c-5e6c9da07c45}
The URI "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier" is a known claim and can be accessed using Claims.Name and I can use a statement like this to extract the objectId from the claims:
Guid userId = new(this.User.FindFirstValue(ClaimTypes.NameIdentifier));
But on the client, the objectId claim is returned using the 'oid' URI and I can't use the same kind of pre-defined claims that I can on the server.
Obviously I can define these as constants in a shared module, but I can't be the only one trying to parse claims from the client side. Is there an analog of 'ClaimTypes' for MSAL on the client side?

I was searching for the same thing and found the ClaimConstants.ObjectId (see here) to match the oid claim type (http://schemas.microsoft.com/identity/claims/objectidentifier).
Guid userId = new(this.User.FindFirstValue(ClaimConstants.ObjectId));
It's part of the Microsoft.Identity.Web package.

Related

Unable to set client claims when acquiring confidential client application token

I am attempting to use the MSAL python library to call another custom api in Azure(Exposed through AppRegistration with an API scope exposed).
I am writing a daemon application that will make the request.
Following Azure documentation here:
https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-daemon-app-configuration?tabs=python
The last example on this Azure docs suggests you can add assertions about custom claims such as client_ip that would be returned in the token.
Similarly, I would like the preferred_username claim to be set to Test as an example:
app = msal.ConfidentialClientApplication(
config["client_id"], authority=config["authority"],
client_credential={"thumbprint": config["thumbprint"], "private_key": open(
config['private_key_file']).read()},
client_claims={"preferred_username": "Test"}
)
However, When I acquire the token using the following code, the preferred_username claim is not within the Token.
result = app.acquire_token_for_client(scopes=config["scope"])
Within the app registration for the daemon app I have added preferred_username as an optional claim (for access tokens).
I am not sure what is wrong with my approach or if I have misinterpreted the intent of client_claims?
I tried to reproduce the same in my environment and got the results like below:
I created an Azure AD Application and configured custom preferred_username claim:
I generated the token via Postman by using below parameters:
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:ClientID
client_secret:ClientSecret
scope:https://graph.microsoft.com/.default
grant_type:client_credentials
Optional claims are not included in the token like below:
Note that: Getting optional claim is only possible with Authorization code flow, ROPC flow, Implicit flow. Currently, Client Credentials flow does not support adding any additional custom claims.
Client Credentials flow fetch the token in the application's context and won't have any user-related claims like preferred_username, given_name or email, etc. So, you have to generate the token in the user's context to get the claims.
Alternatively, I generated the Access Token using the endpoint like below:
https://login.microsoftonline.com/tenantID/oauth2/v2.0/authorize?client_id=ClientID&response_type=token&redirect_uri=redirecturi&scope=user.read&response_mode=fragment&state=12345&nonce=678910
Optional claims are included in the token like below:
Reference:
Client assertions (MSAL) - Microsoft Entra | Microsoft Learn

Which Azure AD property (from login response) store as UserId in the database?

Upon successful login with Azure AD (via MSAL 2), I get the following object:
environment:"login.windows.net"
homeAccountId:"ID_1.ID_2"
idTokenClaims:Object
aio:"xxx"
aud:"xxx"
exp:1661778482
iat:1661774582
idp:"https://sts.windows.net/ID_2"
iss:"https://login.microsoftonline.com/TENANT_ID"
name:"xxx"
nbf:1661774582
nonce:"xxx"
oid:"ID_3"
preferred_username:"xxx"
rh:"xxx"
sub:"xxx"
tid:"xxx"
uti:"xxx"
ver:"xxx"
localAccountId:"ID_3"
name:"xxx"
nativeAccountId:undefined
tenantId:"TENANT_ID"
It seems that according to the documentation, if I understand correctly, you need to take idTokenClaims.oid which is equal to localAccountId.
But I thought that if you want real permanent UserId to store in database, then you should look at the properties of the user through the Azure portal, and takes its Object ID field. I am a bit confused that Azure user's Object ID field also existed in the returned object (upon Azure AD login via MSAL). Object ID is in the homeAccountId field, namely ID_1, but for some reason it is followed by a dot and some other incomprehensible ID_2, which is present in idTokenClaims.idp.
Please tell me what is correct to store in the database as UserId in the CreatedBy field for an Azure AD user when authorizing through MSAL 2:
idTokenClaims.oid, aka localAccountId?
or ID_1 from homeAccountId (which equals to the user's Object ID) ?
Object ID i.e. oid is the primary claim that you should use as part of a system of records (plus tenant ID i.e. tid if you are serving multiple tenants).
homeAccountId and localAccountId are MSAL SDK specific terms, but they map to claims in the ID token eventually -homeAccountId is "oid.tid", while localAccountId is simply "oid" as it's only used with ADFS which doesn't have tenancy.
The idTokenClaims in the response payload is the actual decoded ID token from Azure AD, and then MSAL adds further useful metadata to the response.
See also:
Microsoft identity platform ID tokens.
Data access sample

Azure AD: How to make tokens have the "hasgroups" claim?

Our application allows assigning permission to groups, which means for every user, we have to reliably determine group membership. The user presents a token regularly obtained with ADAL (some use .NET, others use NodeJS, others use CLI).
Some users seem to be sending a token with the following claim:
"hasgroups": true,
That claim is documented in the Azure AD token reference page.
We would like to add a test case for that, but after following steps here and here, we always end up with a token with the following claims:
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "https://graph.windows.net/{redacted}/users/{redacted}/getMemberObjects"
}
},
What is wrong with our setup? Why can't we get the hasgroups claim?
Here are some additional information:
Application type is Native (not WebApi).
Manifest says "oauth2AllowImplicitFlow": true.
The application is given access to Azure Key Vault.
We use the following code to get the token (in C#):
var userCredential = new UserCredential( _userName, _password );
result = context.AcquireToken( _resource, _clientId, userCredential );
Where:
_userName and _password are from a user with lots of groups.
_clientId is the application id of the native application - the one with "oauth2AllowImplicitFlow": true.
_resource is https://vault.azure.net.
The token is emitted correctly. The only issue is that it shows _claim_names and _claims_sources instead of hasgroups.
Where: • _userName and _password are from a user with lots of groups.
As the user is part of lots of groups (assuming 6 or more here).. Azure AD token will come back with a groups overage indicator instead of actual group ids in “groups” claim. I guess you know that and hence doing it intentionally.
var userCredential = new UserCredential( _userName, _password );
result = context.AcquireToken( _resource, _clientId, userCredential );
Since you're acquiring the token in a .NET based application using C# code, the token response is not really limited in length (like in cases for a web SPA, where it is being returned as a URI fragment and URL length has limits)
Looking at the documentation both "hasgroups" and "groups:src1" claims have the same intention of telling that there are too many groups to return as part of the token. Although there is a subtle difference:
in cases where URL limit applies, "hasgroups" will be sent as true (like implicit grant flow for SPA)
in cases where length is not limited (like in your case), Azure AD will still not return all the groups to make sure the token doesn't get too big, but it will send a little more information on how to get to all groups by sending the information on how you can query for all your groups. In this case it's sending the "groups:src1" and "_claim_sources" with source information instead of just the "hasgroups"
Claims in id_tokens
For anyone looking more on this. Please refer Doc saml-tokens
Note
Source : Azure Sample Link

Generating AAD Token with UPN

I am trying to generate a token for a user with below code.
string apiResourceId = "11224320-66b9-4132-8953-9aa485f07004";
string clientId = "bc9869a0-2393-4e42-8c52-845071640ea8";
Uri redirectUri = new Uri("https://localhost:44335/");
string authority = string.Format("https://login.windows.net/{0}",
"rudderless.onmicrosoft.com");
var authContext = new AuthenticationContext(authority);
AuthenticationResult authenticationResult;
authenticationResult = await authContext.AcquireTokenAsync(apiResourceId, clientId,
redirectUri, new PlatformParameters(PromptBehavior.Auto, null));
I have been getting an error in AcquireTokenAsync call -
AADSTS70002: The request body must contain the following parameter:
'client_secret or client_assertion'. Trace ID:
a198696d-8377-40eb-8351-527a25183500 Correlation ID:
24d4b47d-67bf-46c0-a6b7-a248c434512e Timestamp: 2017-09-20 23:09:38Z
Why do I need a client_secret or client_assertion if I want to generate a token when a user is authenticated against a AAD? The type of Client I am using is "Web app /API". However when I am trying to use a Native client I get the token generated but API call to apResourceID is generating unauthorized error.
Few Questions I am seeking help on related to the scinario -
Why I need to provide client_secret when I am using user auth flow?
Why AcquireToken succeed when I change the client Type to Native?
Why the token generated through native client gives an Unauthorize error?
Is there a way for admin to consent on behalf of every user in AAD?
Why I need to provide client_secret when I am using user auth flow?
Web Apps and APIs are considered Confidential Clients. See here for a definition of the different Client Types in the OAuth 2 Specification. These kinds of client always need to use their client secret to authenticate, no matter the flow they are following.
Confidential clients are typically issued (or establish) a set of
client credentials used for authenticating with the authorization
server (e.g., password, public/private key pair).
Why AcquireToken succeed when I change the client Type to Native?
Native Client Applications are a subset of Public Clients. These are defined, in the specification as:
Clients incapable of maintaining the confidentiality of their
credentials (e.g., clients executing on the device used by the
resource owner, such as an installed native application or a web
browser-based application), and incapable of secure client
authentication via any other means.
Therefore, they do not have or need a client_secret to authenticate... but this also means they can only authenticate with user context, whereas a confidential client could authenticate without a user present (Client Credential Flow).
Why the token generated through native client gives an Unauthorize
error?
This is hard to answer without knowing more about the error and the call you are making that causes this error. You should provide more information about this scenario.
Is there a way for admin to consent on behalf of every user
in AAD?
Yes. In the new Azure Active Directory V2 Endpoint, we have an "Admin Consent Endpoint".
Using the older V1 endpoint, we have an &prompt=admin_consent query string which you can read about here.

Parsing saml token with node.js

I found a node.js package to parse SAML tokens:
https://github.com/leandrob/saml20
Here's a snippet of the configuration code:
saml.validate(rawAssertion, options, function(err, profile) {
// err
var claims = profile.claims; // Array of user attributes;
var issuer = profile.issuer: // String Issuer name.
});
However, I'm not sure where the rawAssertion parameter comes from or is defined. Is it part of the http request? And if so, which attribute is it?
Additional context - I need this because I am building a node.js app that will use Active Directory Federation Services for authentication purposes.
Thank you in advance!
The assertion is a part of a POST the identity provider issues back to your app. Formally, when your app redirects to the provider and the provider validates the credentials, it returns a form with action pointing back to your app and a tiny javascript that causes the form to be POSTed. The POSTed body contains the token in one of fields (depends on whether SAML 1 or 2 is used).
Example tokens are here
https://www.samltool.com/generic_sso_res.php
this is what you extract from the POST and feed to the saml20 module.

Resources