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.
Related
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.
I want to be able to create a new user in our Azure B2C instance using their preferred email address as the username they will use when accessing our web portal.
I'm using the Invitation Microsoft Graph API to invite new users This sends them an email and they then signup with us. This however assigns them a unique username using a combination of their email and our domain i.e. myemail_adomain.com#EXT##our_verified_domain.com.
This leads to a terrible UX as users need to remember this very unmemorable username. Remembering passwords is enough of a challenge for users as it is.
If I create a user inside the Azure B2C portal I can give them any email address I want and not one of our verified domains using Create Azure AD B2C user
I want to be able to use this method but via an API.
The first 2 options, Create and Invite user, are available via the Microsoft Graph Inviations API
and the Create User API but I can't find a way to do option 3.
The Create API won't allow unverified domains and the Invite API creates the unique username which is very user unfriendly.
Does anyone know how I can do this?
The other option is to get them to signup themselves via a signup user flow but I'd rather avoid this as I want control over who is allowed to sign up.
• You can surely create a user in Azure AD B2C tenant through Microsoft Graph API by following the below documentation link for that purpose: -
https://learn.microsoft.com/en-us/graph/api/user-post-users?view=graph-rest-1.0&tabs=http
Ensure that you have ‘User.ReadWrite.All’ and ‘Directory.ReadWrite.All’ permissions for ‘Application’ and ‘Delegated’ permissions type and the same permissions are consented for Microsoft Graph API in the explorer also with ‘Admin Consent’. Once, these are done, then execute the below command in Graph API as shown below: -
POST https://graph.microsoft.com/v1.0/users
Content-type: application/json
{
"accountEnabled": true,
"displayName": "Adele Vance",
"mailNickname": "AdeleV",
"userPrincipalName": "AdeleV#contoso.onmicrosoft.com",
"passwordProfile" : {
"forceChangePasswordNextSignIn": true,
"password": "xWwvJ]6NMw+bWH-d"
}
}
As you can see, I don’t have sufficient privileges, so I can’t create a user in Azure AD B2C tenant.
The output will be as below after successful execution of the above Graph API command: -
HTTP/1.1 201 Created
Content-type: application/json
{
"#odata.context": "https://graph.microsoft.com/v1.0/$metadata#users/$entity",
"id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
"businessPhones": [],
"displayName": "Adele Vance",
"givenName": "Adele",
"jobTitle": "Product Marketing Manager",
"mail": "AdeleV#contoso.onmicrosoft.com",
"mobilePhone": "+1 425 555 0109",
"officeLocation": "18/2111",
"preferredLanguage": "en-US",
"surname": "Vance",
"userPrincipalName": "AdeleV#contoso.onmicrosoft.com"
}
I'm following the Microsoft tutorial Create a user (local or social account). So I'm trying to create a user from HTTP call, for this I'm sending a similar payload to the tutorial:
{
"accountEnabled": true,
"creationType": "LocalAccount",
"displayName": "Alex Wu",
"passwordProfile": {
"password": "Test1234",
"forceChangePasswordNextLogin": false
},
"signInNames": [
{
"type": "userName",
"value": "AlexW"
},
{
"type": "emailAddress",
"value": "alexw#gmail.com"
}
],
"userIdentities": [
{
"issuer": "google.com",
"issuerUserId": "MATxTNg5MzYyMzMyMNY1Njc="
}
]
}
My question is how I can generate the issuerUserId as it is necessary for the Google supplier to recognize the user. I'm trying with a random value encode with base64 but when I run the user flow the user it's created again with a duplicate email. I suppose Google don't recognize the issuerUserId.
Update:
Base on Allen Wu answer:
issuerUserId is a unique user identifier for the issuer. You can set any valid string (don't be duplicate) for it.
I create this issuerUserId with a valid string: 12345678909823456789
As before, I create successfully the user and the source show as Google:
But when I want to log in the account with Google provider
The user is duplicated:
I assume instead of launch Sign in process Azure/Google don't recognize the account's issuerUserId and launch the Sign-up process, for that reason that's why I think issuerUserId might be created by Google.
Some notes:
I'm changed the emails for demo emails, but that is the current
behavior.
I'm only using Google authentication, I don't using email and password fields of the login, because the purpose of the app is only for Social Authentication (Google specifically)
I'm using Sign up and sign in (Recommended) user type flow.
If you have more thoughts about issuerUserId that can help me, let me know, I'll really appreciate.
It's not true.
Based on my test, the user flow won't create a new user which has a duplicate email. (the previous user is created via AAD Graph)
issuerUserId is a unique user identifier for the issuer. You can set any valid string (don't be duplicate) for it.
And Google / Facebook or any other social idp won't verify it, because this user is created in B2C. It's an B2C local account. ("creationType": "LocalAccount")
Create an B2C local account doesn't mean this user has been created in Google.
I guess that you add Google idp into the user flow and are trying to create a new user in Google rather than B2C.
You should click on the "Sign up now" in user flow to create the local account. Kindly check it.
Update:
As I have mentioned above, the user you created with Azure AD graph is an B2C local account. You should use the default sign in feature to log into that user. B2C will verify your credential.
But when you click on the "Google" to sign in, in fact the Google will verify your credential and will associate your Google account to a new B2C account. It's not a local account.
So they are two different accounts. You can verify this by changing the password of the B2C local account. After changing the password, you still need to use the old password to sign in with the "Google" option. But you will need to use the new password to sign in with the first user (B2C local account).
You can use GET https://graph.windows.net/myorganization/users?api-version to get the two users and find that the issuerUserId of them are different.
I have a few users added to my Azure AD account, I would like to get the roles and user information on these users by calling an Azure API from Postman in the form of claims. I tried calling the following URL with the parameters as :
https://login.microsoftonline.com/myTenantId/oauth2/token
Body:
grant_type : password,
client_id : client id,
client secret : client secret
I receive the access_token in the encoded format in the response, When I decode it on https://jwt.io/ I see the decoded data, but there's no user roles in the access_token.
I would like to get the user information and the roles in the form of claims in same response.
What approach would I need to take on this ?
If the role you mentioned refers to directory role, the answer is no, it won't be returned in the token. Just like juunas said, you can call graph api to get directory role information.
If the role you mentioned refers to application role, the answer is yes, you can get the role information in id_token. The prerequisite is that you have assigned some roles to the user.
Here are the detailed steps. You can also refer to this article.
edit the manifest to add some custom roles.
Something like this.
{
"allowedMemberTypes": [
"User"
],
"displayName": "Test",
"id": "c200e304-fff3-49f1-a4df-e406741ea680",
"isEnabled": true,
"description": "Bla bla",
"value": "test"
}
2.assign users to roles.
Click Enterprise applications->All applications->
Click your application->click Users and groups->click Add user
role assign.
Here is the request to get id_token.
You will find the roles in id_token.
I need to programmatically add user account to Azure API Catalog portal.
I use ApiManagementClient class from package Microsoft.Azure.Management.ApiManagement (3.4) and if collection Users does not have a user with a particular e-mail (Azure Active Directory has a user whose username is our company e-mail, for every user that we have in or internal Active Directory)
then I'd try to call Users.CreateAsync which will take a parameter of type UserCreateParameters that consists of Email, FirstName, LastName, State and Password.
The issue here is that my user has been added to the portal manually by administrator and my password was not required for that (as it is an AAD user)
but this method won't let me create a new user if I don't provide a password.
How can I add an AAD user to the API Catalog, from code and without knowing the password. Otherwise, I won't be able to add an API subscription for a user that has not been added to the portal, yet.
We are working a new nuget package, which has support for that. For now, you can use the rest api https://learn.microsoft.com/en-us/rest/api/apimanagement/user/createorupdate
But you would need to know the unique Id of the User in AAD System. The operation above will create a user which can log-in both using AAD or Basic Auth.
{
"properties": {
"firstName": "foo",
"lastName": "bar",
"email": "foobar#mytenant.onmicrosoft.com"
"identities" :[
"provider": "Aad",
"id": "<unique id in AAd Tenant>"
]
}
}