Azure-Samples / active-directory-dotnet-desktop-msgraph-v2

Sample showing how a Windows desktop .NET (WPF) application can get an access token using MSAL.NET and call the Microsoft Graph API or other APIs protected by the Microsoft identity platform (Azure Active Directory v2)
https://aka.ms/aadv2
MIT License
146 stars 98 forks source link

How to add custom user defined claims to azure ad access token. #50

Closed John12x closed 4 years ago

John12x commented 4 years ago

I want to add custom user defined claim like moviename to be added to access token. I want to have claim addition logic to be present even before token generation logic. I read few articles like claim mapping or optional claims but didnt get it work for my requirement. Could you please guide me.

TiagoBrenck commented 4 years ago

Do you want Microsoft Graph to issue you additional claims in its access token?

John12x commented 4 years ago

I have a requirement where end-user who gets an authorized token can use custom user-defined claims present in token for his own logic. Actual Scenario is, my webap get() method will return an access token.End user will call this webpi endpoint to get token.The custom claims present in the token, will be used by end user for his requirement.

I have followed below articles, https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims https://rasmustherkelsen.wordpress.com/2019/12/09/custom-claims-in-azure-ad/ https://www.rahulpnath.com/blog/azure-ad-custom-attributes-and-optional-claims-from-an-asp-dot-net-application/

Kindly let me know, If I'm going in proper direction, to add custom userdefined claims to aad jwt access token.

TiagoBrenck commented 4 years ago

So, the first link is not what you are looking for, because the claim moviename was created by you. That link is to include additional pre-existing claims.

What you have to do first, is to map this custom moviename claim using claims mapping in Powershell. To accomplish this, follow this link: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping#example-create-and-assign-a-policy-that-uses-a-claims-transformation-in-tokens-issued-to-a-service-principal

Azure gives you 15 extension attributes, look at this table to support scenarios like yours.

Your steps are:

  1. Use New-AzureADPolicy command to create your custom policy mapping
  2. Assign the new policy to a service principal ID that you like it to return the new policy
John12x commented 4 years ago

@TiagoBrenck Since I'm generating aad jwt access token from webapi get() based on client id and client secret, I will not have any user info. Also, I need to update value of custom claim moviename with value obtained from external source before generating access token.Could you pls provide any samples that fits my requirement.

Also it would be a great help, if you share exact Powershell commands according to my requirement for creating policy. I'm bit confused with the commands.

TiagoBrenck commented 4 years ago

Ok, lets do it by steps.

First, follow the prerequisites to run the powershell commands.

If you already have AzureAD powershell module installed, you will have to uninstall it: Uninstall-Module -Name AzureAD

Then, your Powershell command to create the custom claim mapping, and using extensionattribute1 field to store it, would be:

New-AzureADPolicy -Definition @('
{
    "ClaimsMappingPolicy":
    {
        "Version":1,"IncludeBasicClaimSet":"true", 
        "ClaimsSchema": [{"Source":"user","ID":"extensionattribute1","SamlClaimType":"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/moviename","JwtClaimType":"moviename"}]
    }
}') -DisplayName "MovieNameExtraClaims" -Type "ClaimsMappingPolicy"

Then, get the new policy mapping id:

Get-AzureADPolicy

Then, apply this policy to your WebAPI service principle ID (You can find it on Azure Portal> Enterprise Applications> Your Web API):

Add-AzureADServicePrincipalPolicy -Id <ObjectId of the Web API ServicePrincipal> -RefObjectId <ObjectId of the Policy>

To check if it succeed, you must see the policy it by running this command:

Get-AzureADServicePrincipalPolicy -Id <ObjectId of the Web API ServicePrincipal>

To test it, lets hardcode a value to your user. For that, go to Microsoft Graph Explorer, sign in with your tenant admin account:

  1. Select Patch option, for the request
  2. Select beta for the api version
  3. Use the /me endpoint: https://graph.microsoft.com/beta/me
  4. For Request Body, paste this
    {"onPremisesExtensionAttributes":{"extensionAttribute1":"Titanic"}}
  5. Then select Run Query
  6. To confirm, run a GET request in the same endpoint and check the extensionAttribute1 value

By this point, if you have the claims mapping creation part done. Next step is to have two adjustments in your web api application registration. For that:

  1. Go to Azure Portal > App Registration > Your Web API
  2. Select Manifest and set "acceptMappedClaims": true, and Save
  3. Then, go to Expose an API.
    • Under Application ID URI, if your value is using the pattern api://<GUID>, you must change to https://<yourTenantDomain>. i.e: https://contoso.onmicrosoft.com
    • If you had to change the Application ID URI, remember to update the scope in your sample code as well.

Now, you can test the application, sign-in with your admin account and acquire an access token to your web api. The new claim will be presented there.

John12x commented 4 years ago

@TiagoBrenck my access token is aad JWT token not SAML token. "SamlClaimType" mayn't be applicable to me as my token is JWT token. Also, I want to update custom claim value through c# code not through powershell .Kindly assist further.

I don't have any UI to get access token. Enduser will call webapi get() may be through postman to get access token and custom claim need to be included in the returned access token.

"Source":"user" -> I will not have any user information as access token returned to postman call. End user while calling webapi get() to get access token, he will pass some parameters in headers . I need to take value from headers, and bind it to custom claim moviename.

Custom claim moviename value, will change dynamically based on webapi request header value.The value may be unique for each request. The custom claim value may be different for each request.

Example:

If customer A, calls webapi get() through postman, by passing headers having moviename property value as TOQ1, then I need to bind TOQ1 to custom claim moviename in code and return access token. So, returned token in postman for customer A will have custom claim moviename value as TOQ1.

At the same time if customer B, calls webapi get() through postman with different header value to moviename property as TOQ2, then I need to bind TOQ2 value to custom claim moviename and return it in access token. So, customer B , will have custom claim moviename value as TOQ2 in claims of access token.

Here 2 different users have called webapi get() at same time with 2 different header values. Access Token need to be returned to these customers with two different custom claim values i.e TOQ1, TOQ2.

How can I achieve above scenario.

John12x commented 4 years ago

@TiagoBrenck Could you please guide me on my scenario.

kalyankrishna1 commented 4 years ago

Hi @Chandu12x , today AAD does not support custom claims from users. It'll be something we wish to make available sometime late this year or early next. Today, your best bet is to process the token you receive from AAD, build a new token with all the claims from the first one and add additional claims to be sent to the next receiver in line.

John12x commented 4 years ago

@kalyankrishna1 As you mentioned " build a new token with all the claims from the first one and add additional claims to be sent to the next receiver in line. "

How can I create a new token based on existing claims. Is it possible to create new token based on existing claims using AAD itself ?

It would be a great help if you provide any useful links or any info on steps to be followed on implementing that.

Kindly suggest.

kalyankrishna1 commented 4 years ago

Internet is your friend. One example (https://houseofcat.io/tutorials/csharp/identity/createjwt). It’s just coding

John12x commented 4 years ago

@kalyankrishna1 I have gone through few posts online. They say the custom claim is possible with below Claims Mapping--> https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-claims-mapping Optional Claims--> https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-optional-claims

https://rasmustherkelsen.wordpress.com/2019/12/09/custom-claims-in-azure-ad/

In the below post, claim value updated with database value.

https://www.rahulpnath.com/blog/azure-ad-custom-attributes-and-optional-claims-from-an-asp-dot-net-application/

Even in this post, @TiagoBrenck mentioned the claim mapping policy to get this done.

You mentioned, "today AAD does not support custom claims from users."

I'm bit confused.

How come they made it work if it's not possible.

Could you please clarify me on this.

TiagoBrenck commented 4 years ago

@John12x there is a difference between these links and my answer vs what seems like you want to do.

We provided you a way on how to have AAD give you extra claims in the JWT token (you missed the powershell command that is not for SAML only), and that is supported. The scenario here is AAD giving you an extra claim, that is also configured on AAD.

You are telling us that this approach is not what you want. We understood that you want to add an extra parameter in the http header, send it to AAD, and have AAD create a JWT token with an extra parameter that is not provided by AAD but by the users. This scenario is not supported.

prezaei commented 3 years ago

@TiagoBrenck and @kalyankrishna1, thanks a lot. Did we ever get to implement this?

We understood that you want to add an extra parameter in the http header, send it to AAD, and have AAD create a JWT token with an extra parameter that is not provided by AAD but by the users. This scenario is not supported.

fernandezjose commented 3 years ago

The issue here is that @John12x wants to create a 'Login Service' where he validates the credentials of the User outside of AD, say an internal database. Then his 'login service' reaches out to Azure using the client-credentials-grant to fetch a token, given that he already confirmed the identity of the user.

The actual user doesn't exist in AD. That's the whole deal.

I had the same issue with one of my recent clients but we had to use Okta.

Okta already figured this out... years ago.

The way Okta did it was by providing a 'callback' mechanism, during the token creation, where you provide those dynamic values and the final token is created with those claims embedded.

I'd love to use all of Azure's services and have it all in one place, but this is a limitation that you need to work on, immediately.

There you go. Get to coding!

VictorioBerra commented 2 years ago

I also need this. @kalyankrishna1 @jmprieur @TiagoBrenck @jennyf19

This is a blocker for us to move from IdentityServer/Duende to AAD. We have tons of non-interactive machine clients (ClientID and Secret ONLY, no user!) that we have applied a few static claims/values to. Auth0, Okta also support this.

This question has popped up in forums, Github issues, StackOverflow. Is there somewhere we can post this to get more visibility and votes to make this a reality?

I would even settle for this being in the manifest, no GUI or anything.

{
    // ...
    "appId": "1234567-abcd-...."
    "appRoles": [],
    "accessTokenStaticClaimDictionary":
    {
        "hello": "world"
    }
    // ...
}
jmprieur commented 2 years ago

@VictorioBerra : for your scenario, wouln't you want to use a daemon app? https://docs.microsoft.com/en-us/azure/active-directory/develop/sample-v2-code#service--daemon

you don't want interactivity. and therefore this is the wrong sample for you.

Can you please send us the links to the GitHub issues, StackOverflow etc ... where this question was asked?

VictorioBerra commented 2 years ago

@jmprieur yes, sorry but this issue came up on Google when searching for "custom static claims to azure ad access token".

And yes "Service / daemon" == "Client credentials grant" == "non-interactive flow" https://oauth.net/2/grant-types/client-credentials/

Would you like me to open an issue on one of the "Service / daemon" samples? I used this issue due to its visibility and title and existing communications with @kalyankrishna1.

For examples of people asking:

One thing I notice is these questions are constantly confused with USER tokens (usually id tokens in OIDC), many times someone just answers "use custom policies" and then the issue gets buried. While the truth is, this is just plain unsupported by AAD.

jmprieur commented 2 years ago

@VictorioBerra thanks for explanation

ThomasVague commented 2 years ago

So there is no way to achieve this at the moment?

stutommi commented 2 years ago

Any ideas on implementing this feature?

klakshmikantha commented 1 year ago

Any update on this issue? Is this even being considered? In order to move into AAD, this becomes a crucial capability.