Obtain user email domain in Azure AD B2C through Graph API - azure

I'm trying to prevent to register more than one user in my app with the same email domain.
So, I'm creating a custom policy on AD B2C to connect with the Azure Function that checks through Graph API if the domain exists on the tenant.
I'm trying to use this approach:
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var users = await graphClient.Users
.Request()
.Filter("identities/any(c:c/issuerAssignedId eq 'j.smith#yahoo.com' and c/issuer eq 'My B2C tenant')")
.Select("displayName,id")
.GetAsync();
The problem is that I cannot change the eq value to contains to filter by domain, and endsWith functions do not work in AD B2C tenants.

You can try by matching the whole content in StartsWith()
var users = await graphClient.Users
.Request()
.Filter("startswith(identities/issuerAssignedId,'j.smith#yahoo.com')")
.Select("displayName,id")
.GetAsync();
please see the official doc for more info - https://learn.microsoft.com/en-us/graph/api/user-list?view=graph-rest-1.0&tabs=http#request-2

Related

Microsoft-Graph - As an Azure AD Admin how you can get a valid access_token for another user

If I understood the response from user #MarcLaFleur here: Resetting a user's password using Microsoft Graph, if you are an Azure AD admin and want to reset a password of another user using Microsoft Graph API then you need to have a valid access_token for the user with Directory.AccessAsUser.All permission, and then you can update the user's passwordProfile.
Question: Using Microsoft Graph, as an Azure AD Admin, how can we get access_token for another user?
Authentication Page of my App Registration:
If you are an Azure AD admin and want to reset the password of another user using Microsoft Graph API, you just need to get the token for the admin account itself, not the user you want to change.
In this case, you could use the auth code flow.
1.In your AD App, add the permissions like below -> click Grant admin consent for xxx button.
2.Login your admin account with the url below in the browser.
https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize?
client_id=<client-id>
&response_type=code
&redirect_uri=<redirect_uri>
&response_mode=query
&scope=https://graph.microsoft.com/.default
&state=12345
3.Use the code to get the token.
4.Use the token to change the password of a normal user.
You could also use the Microsoft Graph SDK, use Authorization code provider.
Something like below:
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithRedirectUri(redirectUri)
.WithClientSecret(clientSecret) // or .WithCertificate(certificate)
.Build();
AuthorizationCodeProvider authProvider = new AuthorizationCodeProvider(confidentialClientApplication, scopes);
GraphServiceClient graphClient = new GraphServiceClient( authProvider );
var user = new User
{
PasswordProfile = new PasswordProfile
{
ForceChangePasswordNextSignIn = true,
Password = password,
}
};
await graphClient.Users[userId]
.Request()
.UpdateAsync(user);

Calling MS Graph API for additional attribute from within B2C IEF Policy

I am trying to prevent duplicate accounts from being created in Azure B2C by looking at a custom field (i.e. Employee ID) and if there is a record of that Employee ID already existing, not allowing the user to create a new account. Since it doesn't appear you can use custom fields as an Input, I was considering using the Graph API to see if a record is returned for a given Employee ID- if it does, then it would stop the registration process, if it doesn't the registration can continue.
Is it possible to directly call the Graph API and do this record count comparison within a policy. Or would I need to have a separate script/function to call Graph API with the given employee ID and then return the number of records to compare?
During sign up, store the employeeId value in the signInNames attribute using a custom policy. This attribute has a uniqueness constraint.
You can acheive this using Microsoft Graph SDK.
Any request to the Microsoft Graph API requires an access token for authentication. The solution makes use of the Microsoft.Graph.Auth NuGet package that provides an authentication scenario-based wrapper of the Microsoft Authentication Library (MSAL) for use with the Microsoft Graph SDK.
For instance, you are using C# to achieve this, Program.cs code snippet will look like:
// Read application settings from appsettings.json (tenant ID, app ID, client secret, etc.)
AppSettings config = AppSettingsFile.ReadFromJsonFile();
// Initialize the client credential auth provider
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(config.AppId)
.WithTenantId(config.TenantId)
.WithClientSecret(config.ClientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
// Set up the Microsoft Graph service client with client credentials
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
UserService.cs:
public static async Task ListUsers(GraphServiceClient graphClient)
{
Console.WriteLine("Getting list of users...");
// Get all users (one page)
var result = await graphClient.Users
.Request()
.Select(e => new
{
e.employeeId
})
.GetAsync();
foreach (var user in result.CurrentPage)
{
Console.WriteLine(JsonConvert.SerializeObject(user));
}
}
If you get result as an existing employee, you will invalidate the login and send error response with an error message.

B2B users cannot sign in to Tenant using v2.0 endpoint & MSAL Auth flow

I am trying to create a B2B Management portal. I've started off with this sample since it uses MSAL and Graph API.
user#live.se is in the tenant. It's been invited as a "guest user", i.e a B2B user. However, signing in with user#live.se does not work even though it's been added to the tenant. Following error after sign-in:
AADSTS50020: User account 'user#live.se' from external identity provider 'live.com' is not supported for api version '2.0'. Microsoft account pass-thru users and guests are not supported by the tenant-independent endpoint. Trace ID: 2ad8bee0-d00a-4896-9907-b5271a113300 Correlation ID: 0ea84617-4aa1-4830-859f-6f418252765e Timestamp: 2017-10-03 15:35:22Z
I changed the authority (from common) to only allow users from my tenant (requirement):
https://login.microsoftonline.com/tenant.onmicrosoft.com/v2.0
Do guests not count as part of my tenant when using MSAL? that would mean I have to use "old" tech, i.e ADAL and AAD Graph, which is not recommended, and feels kinda lame.
If you pass the specific tenant value in the authority, then
Only users with a work or school account from a specific Azure AD tenant can sign in to the application. Either the friendly domain name of the Azure AD tenant or the tenant's GUID identifier can be used.
That's means the Microsoft Account is not supported in this scenario. Refer here for the Microsoft Account and Work or school accounts. And in this scenario, if you new a user user from other tenant, it should also works.
You can refer the document for tenant from link below:
Fetch the OpenID Connect metadata document
I know this is an old thread but just in case anyone stumbles upon it, here is a solution:
In cases of Personal guest accounts, use Credential Grant Flow (Get access without a user).
To do that, you would first need to grant appropriate permission (of Type Application) for the API you wanted to use on behalf of the signing user. This would let you acquire access token with the application's identity itself rather than the signed in user.
Next get token like this (in this sample, I'm getting access token for Graph API):
public async Task<string> GetAccessToken()
{
using (HttpClient httpClient = new HttpClient())
{
string token = "";
try
{
httpClient.BaseAddress = new Uri($"https://login.microsoftonline.com/{tenantId}");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
HttpRequestMessage request = new HttpRequestMessage();
List<KeyValuePair<string, string>> body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"),
new KeyValuePair<string, string>("client_secret", appSecret),
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
request.Method = HttpMethod.Post;
request.RequestUri = new Uri($"{httpClient.BaseAddress}/oauth2/v2.0/token");
request.Content = new FormUrlEncodedContent(body);
var response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsAsync<dynamic>();
token = content.access_token;
}
catch (Exception e)
{
}
return token;
}
}
Tip: If your goal is also Graph API, don't try to get logged in user info by using the /me endpoint in this case. Since the token was generated using the application identity rather than the signed in user, /me would be the application not the logged in user. What you want to do is: retrieve logged in user id from the Claim (Type: http://schemas.microsoft.com/identity/claims/objectidentifier) and use the /user/{userid} endpoint.
I found: for personal accounts (Get access without a user) in the body of the request you must to use grant_type = 'client_credentials' and for corporate accounts to use grant_type = 'authorization_code'

Invite user once created in the AD B2C to sign on my app

I have this scenario working properly on ADB2C following this tutorial
I can create users using ADAL.
(here is the context for the scenario)
My goal is to send user invitation (email) to get him on board on my appp.
I found that on graph.microsoft.com (the MS Graph and not the AD graph) there is the invitation manager that can be used for that purpose and may be is the way that invitation gets triggered if you create user on the B2C azure portal.
So Do I need to give permissions to my app (the same way I did to get
access token on AD graph to manage users) ?
Should I acquire the token on the MS graph the same way I did for AD
AuthorizationCodeReceived = async (context) =>
{
// get authentication context
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{AuthenticationHelper.Tenant}", new NaiveSessionCache(userObjectID));
ClientCredential credential = new ClientCredential(AuthenticationHelper.ClientId, AuthenticationHelper.AppKey);
AuthenticationResult result = await authContext.AcquireTokenAsync("https://graph.windows.net", credential);
// ----> Token used on the authorization header for AD user management and work properly
AuthenticationHelper.Token = result.AccessToken;
// Token for MS graph
ClientCredential MSCredential = new ClientCredential(AuthenticationHelper.MSClientId, AuthenticationHelper.MSAppKey);
AuthenticationResult resultMSGraph = await authContext.AcquireTokenAsync("https://graph.microsoft.com", MSCredential);
// ----> Token used on the authorization header for MS Graph and is not working !!
AuthenticationHelper.MSGraphToken = resultMSGraph.AccessToken;
},
Thanks for your help
The invitation manager API on the Microsoft Graph is not supported for Azure AD B2C.
At this time, the invitation manager API is intended for enterprise/regular Azure AD tenants to invite other users as guests (see Azure AD B2B Collaboration).
There is already an entry in the Azure AD B2C UserVoice forum asking for the ability to send email invitation for new users to sign up. I'd recommend you vote for this entry to help us prioritize it and also to stay up to date on it's progress.
In the interim, you'll have to implement this yourself, either a simple welcome email or a more complex "redeem code" workflow.

Integrate Azure AD B2C with Xamarin App

My Xamarin App (PCL) calls a Web API as shown in the code below:
AuthenticationResult ar = await new AuthHelper().AcquireTokenSilentAsync();
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(Settings.ApiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", ar.Token);
using (HttpResponseMessage response = await client.GetAsync("api/job"))
{
response.EnsureSuccessStatusCode();
using (HttpContent content = response.Content)
{
string result = await content.ReadAsStringAsync();
return result != null ? JsonConvert.DeserializeObject<ObservableCollection<JobTask>>(result) : null;
}
}
}
My Web API is authorized as follows:
[Authorize(Roles = "Admin,BusinessAdmin")]
I am using Azure AD B2C to obtain the token. I am able to get the user roles from Azure AD Graph. What I am unsure about is how to add the roles that are returned from the Graph query into the AuthenticationResult.Token that gets passed to the Web API.
Azure AD B2C does not currently have first class support for application roles nor groups as claims in the token.
You can request this feature in the Azure AD B2C Feedback Forum
Alternatively, you can implement this yourself via Custom Policies. To do this you would add a step in your User Journey that calls out to the Graph to obtain either of these and adds them as claims to the token. See this article for more info.

Resources