How to differentiate between 'local' and 'social' user types after login via Azure AD B2C? - node.js

I have a Node.js/Express application that uses Azure AD B2C for authentication.
Currently, two types of users can login:
Local accounts - EXTERNAL users in the Azure AD B2C tenant
Social accounts - INTERNAL users from a single Azure AD tenant
Desired Behaviour
I want to display different frontend user interface elements based on the type of user that has logged in.
Local accounts will be able to call endpoints relevant to EXTERNAL users
Social accounts will be able to call endpoints relevant to INTERNAL users
What is the recommended way to differentiate between who logged in?
I can see that the authToken object that is returned from msal-node's acquireTokenByCode() method is different depending on which type of user logged in.
Should I use a property from the authToken object to differentiate between user types?
If so, which property is the best one to use?
And, so that I can perform relevant UI actions accordingly, is it acceptable to store this value as a cookie in the browser (eg 'user_type': 'EXTERNAL' or 'user_type': 'INTERNAL') using something like js-cookie?
Below is a redacted version of the authToken object that is returned after login.
All values are the same for different users types, unless otherwise specified.
Notably, these two properties are only present after a 'social account' login:
idp_access_token
idp
{
"authority": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<my-azure-ad-b2c-tenant>.onmicrosoft.com/b2c_1_signin1/",
"uniqueId": "*******", // <-- different for each user, this is the object id of the user as represented in the Azure AD B2C tenant
"tenantId": "",
"scopes":
[
"https://<my-azure-ad-b2c-tenant>.onmicrosoft.com/my-web-app-api/tasks.write", // <-- these are the api permissions granted to my web app
"https://<my-azure-ad-b2c-tenant>.onmicrosoft.com/my-web-app-api/tasks.read" // <-- these are the api permissions granted to my web app
],
"account":
{
"homeAccountId": "***uniqueId***-b2c_1_signin1.<the-directory-id-of-my-azure-ad-b2c-tenant>", // unique id is different for each user
"environment": "<my-azure-ad-b2c-tenant>.b2clogin.com",
"tenantId": "",
"username": "<user-email-address>", // different for each user
"localAccountId": "***uniqueId***", // different for each user
"idTokenClaims":
{
"exp": 1673069519,
"nbf": 1673065919,
"ver": "1.0",
"iss": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<the-directory-id-of-my-azure-ad-b2c-tenant>/v2.0/",
"sub": "***uniqueId***", // different for each user
"aud": "<the-application/client id of my web app>",
"iat": 1673065919,
"auth_time": 1673065918,
"idp_access_token": "********", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"idp": "https://login.microsoftonline.com/<the-directory-id-of-my-azure-ad-tenant>/v2.0", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"emails":
[
"<user-email-address>" // different for each user
],
"tfp": "B2C_1_signin1",
"at_hash": "*******" // different for each user
}
},
"idToken": "*******", // different for each user
"idTokenClaims":
{
"exp": 1673069519,
"nbf": 1673065919,
"ver": "1.0",
"iss": "https://<my-azure-ad-b2c-tenant>.b2clogin.com/<the-directory-id-of-my-azure-ad-b2c-tenant>/v2.0/",
"sub": "***uniqueId***", // different for each user
"aud": "<the-application/client id of my web app>",
"iat": 1673065919,
"auth_time": 1673065918,
"idp_access_token": "********", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"idp": "https://login.microsoftonline.com/<the-directory-id-of-my-azure-ad-tenant>/v2.0", // <-- this property is ONLY present in the authToken returned from the SOCIAL login
"emails":
[
"<user-email-address>" // different for each user
],
"tfp": "B2C_1_signin1", // different for each user
"at_hash": "*******"
},
"accessToken": "*******", // different for each user
"fromCache": false,
"expiresOn": "2023-01-07T05:31:57.000Z",
"correlationId": "*******", // different for each user
"requestId": "",
"extExpiresOn": "2023-01-07T05:31:57.000Z",
"familyId": "",
"tokenType": "Bearer",
"state": "",
"cloudGraphHostName": "",
"msGraphHost": "",
"fromNativeBroker": false
}
For Reference
For reference, below are the steps I took to:
Enable login from a single Azure AD tenant
Enable Azure AD users to be able to call Graph API in their own tenant
I added a single Azure AD tenant as an Identity Provider by going to:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > Identity Providers > + New OpenID Connect provider > [ fill in required fields and click 'Save' ]
Detailed steps on adding an Azure AD tenant as an identity provider can be found here:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-azure-ad-single-tenant?pivots=b2c-user-flow#configure-azure-ad-as-an-identity-provider
In the Scope field, I added the scopes that I wanted the Azure AD users to be able to use in their own tenant, i.e:
openid profile User.ReadWrite.All Directory.ReadWrite.All Team.ReadBasic.All TeamSettings.ReadWrite.All
Then I went to the relevant 'user flow' at:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > User flows > B2C_1_signin1 > Identity providers > [ check the recently added OpenID connect provider ] > [ click 'Save' ]
In order for Azure AD users to be able to make Graph API calls to their own tenant, I did the following to make sure that an idp_token was returned when they logged in - I believe this is essentially an 'access token' for their tenant:
Azure Portal > Azure AD B2C Tenant > Azure AD B2C > User flows > B2C_1_signin1 > Application claims > [ select 'Identity Provider Access Token' ] > [ click 'Save' ]

The idp claim is the one that you are looking for since it will be set of all external providers of the Azure AD B2C tenant.
Note that the idp_access_token claim is present when pass through is enabled but is only supported for OAuth 2.0 providers, so it may be blank for some.

Related

Azure Ad B2C vs Keycloak

I have a question regarding keycloak and Azure Ad b2c. Both offres authentication with different identity providers. Main difference I can see is in authorization. With Keycloak I can easily manage claims and roles of users via admin portal. In Azure B2c it looks very hard (setting custom policies) maintaining all the data via custom created app. Am I missing something? Relying app is strongly cloud native (Azure of course) and thats why I'm thinking about azure b2c. Relying app will strongly use roles and claims also.
What do you think about these two identity solutions (pros and cons).
What is the easiest way to manage implemented (by custom policies) user claims and roles in azure b2c? (do I have to write separate management app by myself or there is easiesy solution?)
Edit: first conclusion is that right nów azure requires separate app for connecting to graphapi (via rest or using sdk). Keycloak has it built in. Any pros of using b2c in this case (for c# dev)?
You don't have to use custom policies to use custom claims. Below an example :
On your B2C tenant, go to Azure AD B2C > Manage > User attributes and create a custom attribute; here a client ID attribute :
Then Azure AD B2C > Policies > User flows and create a flow, for example "Sign In" :
Use the recommended flow :
During the flow's creation, at the last steps, you can specific what claim will be included in the returned jwt token on Sign in; select our custom attribute Client ID :
Then when I login into my application using a B2C account, I have the below JWT token returned :
JWT token's payload, base64 decoded :
{
"iss": "https://mytenant.b2clogin.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/v2.0/",
"exp": 1664181788,
"nbf": 1664178188,
"aud": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"oid": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"sub": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"extension_ClientID": 1,
"name": "myUserName",
"emails": [
"user#email.com"
],
"tfp": "B2C_1_SI",
"nonce": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"scp": "user_impersonation",
"azp": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"ver": "1.0",
"iat": 1664178188
}
I have set the "extension_ClientID": 1 using the msGraph API but this could also be done using a SignUp flow.

How to add roles claim in access_token , currently it is coming in id_token?

I am following Authentication code flow with PKCE and my Identity provider is Azure Active directory.
I have created a App , "client-app" from App Registrations. In the manifest I have added appRoles like the following.
"appRoles": [
{
"allowedMemberTypes": [
"User"
],
"displayName": "StoreGroupManager",
"id": "47fbb575-859a-4941-89c9-0f7a6c30beac",
"isEnabled": true,
"description": "Consumer apps have access to the consumer data.",
"value": "StoreGroupManager"
}
]
I am assigning this role StoreGroupManager to Users. Now when I follow Authorization code flow with PKCE and obtain the id_token , refresh token and access_token. I can see that the id_token has a claim roles but not the access_token.
I need to have roles claim claim in the access_token. Can this be possible?
The following is the decoded id_token.
Roles will be in the access token if the app registration for the API that the access token is for defines those roles and they are assigned to the user.
So if you use the same app registration for the client and API, they should be there.
But if you have separate app registrations for the client and API, you will need to define the role in both apps and assign the user to it on both of them as well.

Azure AD - return roles and groups in token SPA application

I created an Angular application with Implicit Grant Flow for authentication and a Web Api in .Net Core 3.1 following this tutorial: https://github.com/Azure-Samples/ms-identity-javascript-angular-spa-aspnetcore-webapi
The problem is: I need to return the roles and/or groups of the logged User within the Bearer Token to authorize my API, but I'm not being able to do so.
I added the roles to the App Registrations Manifest here, added the claim 'groups' in the Token Configuration menu and set the "User assignment required?" as yes in my Enterprise App Configuration.
Even with all these configurations, I'm not able to return the roles/groups claims in the bearer token.
Example of the token the authentication returns:
{
"ver": "2.0",
"iss": "https://login.microsoftonline.com/9188040d-6c67-4c5b-b112-36a304b66dad/v2.0",
"sub": "AAAAAAAAAAAAAAAAAAAAABTOBMzzWB5LS36oSmQMgyc",
"aud": "ecb5e87f-6f34-4f05-8e8d-8d6149178926",
"exp": 1597173984,
"iat": 1597170084,
"nbf": 1597170084,
"name": "name",
"preferred_username": "email#outlook.com",
"oid": "00000000-0000-0000-fa54-d112egdce65a",
"tid": "9188040d-6c67-4c5b-b112-36a304b643ad",
"azp": "edeb4b7d-9cac-4f3b-a21d-ead77993689e",
"scp": "access_as_user",
"azpacr": "0",
"aio": "DYNhHjG*PSY1ceuC11yaLYcLta8zh49iA!l2UCbCsH9QlaUkEHVQ4paQFRmb!qv7J6yTbAQItGWDgCW9UgUipz4Xnma*DOkFFDNIZ5lkffThD*ie91XMzZIoPhUPwNHOt5dLrw3VASE2WCvJfvDFOQk$"
}
Am I doing something wrong? Is there any other way to authorize the logged user in a SPA Application, return the token with the roles and groups and send it to the Web Api?
You should add the App Role into the manifest of the service app (TodoListAPI) rather than the client app (TodoListSPA).
Don't forget the assign the App Role to the users.
This should be able to fix your issue.
Hi You need go to azure ad -> app registrations -> your app -> taken configuration -> Add Groups Claim -> then select Security groups checkbox. Save. Then log out log in again and should be in taken under groups.

How to add a User as a Member from another IDP?

We have ADB2C tenant with a Identity Provider setup to an Okta setup in another Organization via Open ID Connect.
We have a Admin UI to add users. I see that GraphAPI has a createUser which takes a json with Password and changePwdOnFirstUse setting. This is fine to add a direct member to ADB2C.
The problem I have is that, how can I add the User from the other Okta
Organization so that when this user logs in to my App (and is authenticated by Okta), can login to my App.
At present, after authentication from Okta, we see User not found Error.
I suppose, I cannot add this user via Graph API using same createUser method as this user password is not something we are supposed to manage.
How do I add this other Organization user to ADB2C, so that I do not see this "User not found" issue?
Thanks.
Using Azure AD Graph API, you can create an external account user, with the userIdentities property of the user object being set to the sub (subject) claim of Okta's ID token:
{
"accountEnabled": false,
"displayName": "John Smith",
"mailNickname": "john.smith",
"otherMails": [
"john.smith#company.com"
],
"userIdentities": [
{
"issuer": "{okta-id-token-iss-claim-value}",
"issuerUserId": "{okta-id-token-sub-claim-value}"
}
],
"userPrincipalName": "{guid}#{your-tenant-name}.onmicrosoft.com"
}
where issuerUserId must be set to the base64 encoding for the sub claim of Okta's ID token.

How should I get last login timestamp of Azure AD B2C user?

I need to get below details of Azure AD B2C users -
Created Date user account
Last Login
User is Active or De-active
I have explored Azure AD graph API (Get-User) but it throws some exception.
https://learn.microsoft.com/da-dk/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
Still I am not sure that, above information will get from Azure AD B2C graph API or not.
Is there any other way to get the above Azure AD B2C user details?
The below API will provide the response
'https://graph.microsoft.com/beta/users?$select=displayName,signInActivity
{
"displayName": "test",
"id": "bf7a60-3db4-4943-8d45-a7de9276c"
},
{
"displayName": "Admin",
"id": "ae1f1dd6-886a-484b-b46e-e48cce4",
"signInActivity": "#{lastSignInDateTime=2021-03-15T14:06:21Z; lastSignInRequestId=55909a8c-2c13-4e5a-8e23-8952571a0300}"
},

Resources