Integrate Azure AD B2C with Xamarin App - azure

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.

Related

Access Microsoft Graph API from Azure B2C

I have an Azure AD B2C tenant with an application running. It is configured to use only Azure AD and Microsoft Accounts to login. This application is used by App Center Auth.
I want to access some Microsoft APIs (Microsoft Graph API, Azure DevOps API) from my mobile application with the same login. Therefore I added the API permissions (Azure DevOps -> user_impersonation and Microsoft Graph -> User.Read) to my application in my Azure AD tenant (not the B2C tenant) to grant these permissions on login.
If I now try to use the access token after the login in my application to access e.g. the user in Microsoft Graph, I get an Unauthorized error.
// Sign-in succeeded, UserInformation is not null.
var userInfo = await Auth.SignInAsync();
// Get tokens. They are not null.
var idToken = userInfo.IdToken;
var accessToken = userInfo.AccessToken;
Within the same method, I try to get the user photo from Microsoft Graph
var graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
var scopes = new[] { "user.read" };
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, graphAPIEndpoint + "/photo/$value");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.SendAsync(request);
var image = await response.Content.ReadAsByteArrayAsync();
UserImage.Source = ImageSource.FromStream(() => new MemoryStream(image));
Has anyone an advice how to configure the B2C / Azure AD application to get access to these API with the Access Token? Or am I on the complete wrong way?
Just like #juunas said, as of today,you need to use AAD Graph API to access Azure AD B2C tenant, this is different from the Microsoft Graph API.
Here is the document for this: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
Take a look at this documentation published about using the Microsoft Graph API and authenticating against it for B2C instances:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=applications
With recent migration away from login.microsoftonline.com to *.b2clogin.com, if you're using MSAL to obtain the authentication token from the AAD B2C instance, you need to override the authority config, and turn off authority validation, as per this document:
https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/msal-b2c-overview
In JavaScript, this is done like below:
const msalConfig = {
auth: {
clientId: "e760cab2-b9a1-4c0d-86fb-ff7084abd902" //This is your client/application ID
authority: "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_susi", //This is your tenant info
validateAuthority: false
},
};
// create UserAgentApplication instance
const myMSALObj = new Msal.UserAgentApplication(msalConfig);
In C# with the MSAL.NET library, it would be done with the authority URL passed to the PublicClientApplication class constructor.
I was able to consume Graph REST API using the below steps,
Get access token,
POST
URL: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
form-data:
{"client_id": <Registered application clientId>, "client_secret": <Registered application generated secret>, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials"
Response:
{"token_type" : "", "expires_in" : "", "ext_expires_in" : "", "access_token" : ""}
NOTE: Registered application within b2c tenant should have required grants to access Graph API
Pass token to access API resources, ex: to get user details with objectId,
GET
URL: https://graph.microsoft.com/v1.0/users/{objectId}
set header key
"Authorization": "Bearer " ++ {access_token from above step 1.>

How to find Audience field for Active Directory OAuth Authentication? (How to send a post request to DevOps from Azure Logic App?)

Please help me with this problem.
I'm trying to send a post request from Azure Logic App to the DevOps to create a release.
I created an http action in my Logic App, This is the uri for creating a release:
https://vsrm.dev.azure.com/{organization}/{project}/_apis/release/releases?api-version=5.0
I'm using Active Directory OAuth for authentication, which I need to provide tenant, client id, audience and secret.
I'm using tenant, client id and secret of my application in Azure Active Directory, but I'm not sure what to use for audience.
Can some one explain for me how to find this audience field?
Do I need to do other things to connect to my DevOps? or define permissions or any other parameters for header?
There will be two approaches for getting authenticated.
Use Azure AD Authentication.
The resource for DevOps is a static value: 499b84ac-1321-427f-aa17-267ca6975798. But, as the DevOps REST API can only set with delegated permission.
You need to use password grant flow to get token:
The token you get will be a bearer token.
The other option is to use personal access token. You can create one in DevOps portal.
And then use it as following:
try
{
var personalaccesstoken = "PAT_FROM_WEBSITE";
using (HttpClient client = new HttpClient())
{
client.DefaultRequestHeaders.Accept.Add(
new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
Convert.ToBase64String(
System.Text.ASCIIEncoding.ASCII.GetBytes(
string.Format("{0}:{1}", "", personalaccesstoken))));
using (HttpResponseMessage response = await client.GetAsync(
"https://dev.azure.com/{organization}/_apis/projects"))
{
response.EnsureSuccessStatusCode();
string responseBody = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseBody);
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Token you get in this way is a basic token.

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.

Configuring ASP.Net application to support muliti tenant using Azure AD account

I created an ASP.NET MVC application and configured authentication with Azure AD using OpenIDConnect. I created a user in one Azure AD and added the same in another Azure AD with right privilege.
I store the claims returned after the Azure AD authentication, in ADAL cache. I use this claim(token cache)to call various Azure Service Management API.
ClientCredential credential = new ClientCredential(ConfigurationManager.AppSettings["ida:ClientID"],
ConfigurationManager.AppSettings["ida:Password"]);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
AuthenticationContext authContext = new AuthenticationContext(
string.Format(ConfigurationManager.AppSettings["ida:Authority"], organizationId), new ADALTokenCache(signedInUserUniqueName));
AuthenticationResult result = authContext.AcquireTokenSilent(ConfigurationManager.AppSettings["ida:AzureResourceManagerIdentifier"], credential,
new UserIdentifier(signedInUserUniqueName, UserIdentifierType.RequiredDisplayableId));
var token= result.AccessToken;
I have configured my application to support multitenant by adding the following in my Account/SignIn controller/action.
public void SignIn(string directoryName = "common")
{
// Send an OpenID Connect sign-in request.
if (!Request.IsAuthenticated)
{
HttpContext.GetOwinContext().Environment.Add("Authority", string.Format(ConfigurationManager.AppSettings["ida:Authority"] + "OAuth2/Authorize", directoryName));
HttpContext.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
}
Now, upon successful signin, the claims that are returned, belong to the original Azure AD in which the user is initially registered in. Thus, the claims used to call management api for any other Azure AD, in which the user is also added, does not work and throws exception as "Acquire Token failed to obtain token".
I added the name of the other Azure AD to the variable "directoryName" on runtime. This time the claims obtained worked for both the Azure AD.
How to get the SSO for multitenant application, without explicitly mentioning the Azure AD name while signing-in, which will provide me with the claims that can work for all the Azure AD in which the user is registered.
Kindly suggest.
Thanks in advance,
Rahul
I am not sure what your parameter signedInUserUniqueName is, I often write like this to get accesstoken:
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.windows.net/" + Properties.Settings.Default.TenantID);
ClientCredential credential = new ClientCredential(clientId: Properties.Settings.Default.ClientID, clientSecret: Properties.Settings.Default.ClientSecretKey);
AuthenticationResult result = authenticationContext.AcquireToken(resource: "https://management.core.windows.net/", clientCredential: credential);
var token = result.AccessToken;

Resources