We're looking to query users in the azure ad b2c directory and programmatically extract their login email for all types of users. Which we understand to be:
- Standard User
- Guest User (B2B)
- Local User (B2C user who signed up with email)
- Social User (B2C user who signed up with a social account)
when running the api call using the azure ad graph explorer:
https://graph.windows.net/myorganization/users?api-version=1.6
we can see all these users. (data is sanitized a bit)
Example Standard User
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "8b7c468b-fec4-4ff2-b448-64f99f3fa9ff",
"deletionTimestamp": null,
"accountEnabled": true,
"assignedLicenses": [],
"assignedPlans": [],
"city": null,
"companyName": null,
"country": null,
"creationType": null,
"department": null,
"dirSyncEnabled": null,
"displayName": "Global User",
"employeeId": null,
"facsimileTelephoneNumber": null,
"givenName": null,
"immutableId": null,
"isCompromised": null,
"jobTitle": null,
"lastDirSyncTime": null,
"mail": null,
"mailNickname": "global.user",
"mobile": null,
"onPremisesDistinguishedName": null,
"onPremisesSecurityIdentifier": null,
"otherMails": [],
"passwordPolicies": null,
"passwordProfile": null,
"physicalDeliveryOfficeName": null,
"postalCode": null,
"preferredLanguage": null,
"provisionedPlans": [],
"provisioningErrors": [],
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2017-10-31T17:20:29Z",
"showInAddressList": null,
"signInNames": [],
"sipProxyAddress": null,
"state": null,
"streetAddress": null,
"surname": null,
"telephoneNumber": null,
"usageLocation": null,
"userIdentities": [],
"userPrincipalName": "global.user@qa2clientb2ctheaccesshub.onmicrosoft.com",
"userType": "Member"
}
Example Guest User
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "6458e1fc-c27b-40cb-b83d-2124f0999130",
"deletionTimestamp": null,
"accountEnabled": true,
"assignedLicenses": [],
"assignedPlans": [],
"city": null,
"companyName": null,
"country": null,
"creationType": null,
"department": null,
"dirSyncEnabled": null,
"displayName": "displayname",
"employeeId": null,
"facsimileTelephoneNumber": null,
"givenName": "givenname",
"immutableId": null,
"isCompromised": null,
"jobTitle": null,
"lastDirSyncTime": null,
"mail": null,
"mailNickname": "qa_theaccesshub.com#EXT#",
"mobile": null,
"onPremisesDistinguishedName": null,
"onPremisesSecurityIdentifier": null,
"otherMails": [
"qa@theaccesshub.com"
],
"passwordPolicies": null,
"passwordProfile": null,
"physicalDeliveryOfficeName": null,
"postalCode": null,
"preferredLanguage": null,
"provisionedPlans": [],
"provisioningErrors": [],
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2017-10-31T15:36:22Z",
"showInAddressList": null,
"signInNames": [],
"sipProxyAddress": null,
"state": null,
"streetAddress": null,
"surname": "surname",
"telephoneNumber": null,
"usageLocation": null,
"userIdentities": [],
"userPrincipalName": "qa_theaccesshub.com#EXT#@qa2clientb2ctheaccesshub.onmicrosoft.com",
"userType": "Member"
}
Example Local User
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "a941e75d-2c1b-4383-9d6c-783c1d008479",
"deletionTimestamp": null,
"accountEnabled": true,
"assignedLicenses": [],
"assignedPlans": [],
"city": null,
"companyName": null,
"country": null,
"creationType": "LocalAccount",
"department": null,
"dirSyncEnabled": null,
"displayName": "Display Name",
"employeeId": null,
"facsimileTelephoneNumber": null,
"givenName": "Glen",
"immutableId": null,
"isCompromised": null,
"jobTitle": null,
"lastDirSyncTime": null,
"mail": null,
"mailNickname": "98c4f2cf-a452-46a4-a33f-6fb451bc3f59",
"mobile": null,
"onPremisesDistinguishedName": null,
"onPremisesSecurityIdentifier": null,
"otherMails": [],
"passwordPolicies": "DisablePasswordExpiration",
"passwordProfile": null,
"physicalDeliveryOfficeName": null,
"postalCode": null,
"preferredLanguage": null,
"provisionedPlans": [],
"provisioningErrors": [],
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2017-11-03T18:18:36Z",
"showInAddressList": null,
"signInNames": [
{
"type": "emailAddress",
"value": "login.email@example.com"
}
],
"sipProxyAddress": null,
"state": null,
"streetAddress": null,
"surname": "Martin",
"telephoneNumber": null,
"usageLocation": null,
"userIdentities": [],
"userPrincipalName": "98c4f2cf-a452-46a4-a33f-6fb451bc3f59@qa2clientb2ctheaccesshub.onmicrosoft.com",
"userType": "Member"
}
Example Social User
{
"odata.type": "Microsoft.DirectoryServices.User",
"objectType": "User",
"objectId": "917bddd5-40d8-4a25-9a6e-8317a6949b48",
"deletionTimestamp": null,
"accountEnabled": false,
"assignedLicenses": [],
"assignedPlans": [],
"city": null,
"companyName": null,
"country": null,
"creationType": null,
"department": null,
"dirSyncEnabled": null,
"displayName": "Display Name",
"employeeId": null,
"facsimileTelephoneNumber": null,
"givenName": "GivenName",
"immutableId": null,
"isCompromised": null,
"jobTitle": null,
"lastDirSyncTime": null,
"mail": null,
"mailNickname": "unknown",
"mobile": null,
"onPremisesDistinguishedName": null,
"onPremisesSecurityIdentifier": null,
"otherMails": [
"another.login.email@example.com"
],
"passwordPolicies": null,
"passwordProfile": {
"password": null,
"forceChangePasswordNextLogin": true,
"enforceChangePasswordPolicy": false
},
"physicalDeliveryOfficeName": null,
"postalCode": null,
"preferredLanguage": null,
"provisionedPlans": [],
"provisioningErrors": [],
"proxyAddresses": [],
"refreshTokensValidFromDateTime": "2017-11-02T13:48:09Z",
"showInAddressList": null,
"signInNames": [],
"sipProxyAddress": null,
"state": null,
"streetAddress": null,
"surname": "Surname",
"telephoneNumber": null,
"usageLocation": null,
"userIdentities": [],
"userPrincipalName": "cpim_662effe2-cd73-4f4a-8b42-2af5f68b2db1@qa2clientb2ctheaccesshub.onmicrosoft.com",
"userType": "Member"
}
In summary we notice:
Standard User
- the userPrincipalName looks normal
- the login email can be found at: userPrincipalName
- creation type is: null
Guest User
- the userPrincipalName looks to be the external directories upn followed by '#EXT#' followed by this primary domain
- the login email can be found at: otherMails[0]
- creation type is: null
Local User
- the userPrincipalName looks to be some object id followed by this primary domain
- the login email can be found at: signInNames[0].value
- creation type is: LocalAccount
Social User
- the userPrincipalName looks to be 'cpim_' followed by some object id followed by this primary domain
- the login email can be found at: otherMails[0]
- creation type is: null
Although we can see some trends we'd rather not make guesses or bad assumptions. So we have the following questions:
1) What is the best way to know which of the four types of user we're looking at?
2) Is there a simpler way to get the login (ideally as one field, we're trying to build this into a simple mapping)? Other than :
If signInNames[0].value is not null use signInNames[0].value
Elseif otherMails[0] is not null use otherMails[0]
Else userPrincipalName
3) Do other social users behave differently? (We've only done Facebook so far.)
4) For social users what is the best way of knowing which identity provider is used?
UPDATE 11/8:
5) Why don't LocalAccount users get an otherMail value?
6) Why don't non-LocalAccount users get sign-in names? I guess that was added exclusively for B2C?
7) Also I guess end users don't really have the ability to associate social and local accounts today? (unless using an app which leverages graph).
UPDATE 11/8 #2:
8) Is it safe (for now) to assume that I can disambiguate a B2C signed-up individual who can only login with a social account using the following criteria:
- creation type is: null - meaning they're not a Local User
- userPrincipalName is prefixed with 'cpim_' followed by a UUID - meaning they are PROBABLY not a Standard or Guest user
Or would there be a better method?