How does silent access token work for azure b2c - azure

I would like to understand the authentication process for Azure B2C. It is my understanding that up to 50k authentication per months are free and you pay more if it goes over 50k.
I am developing two applications. One for front-end and another for back-end that will use Azure B2C to authenticate users.
Here is my scenario.
Users will go to UI portal to login with their social accounts.
Users can access back-end resources by making API call based on their permissions.
So it is my understanding that when you login to UI using your social account you receive an access token, which is one authentication in B2C. If they want to access back-end resources from API, it requires you to acquire access token by making another request as shown here. Below is the example code that acquires token silently.
// Retrieve the token with the specified scopes
var scope = AzureAdB2COptions.ApiScopes.Split(' ');
string signedInUserID = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier).Value;
TokenCache userTokenCache = new MSALSessionCache(signedInUserID, this.HttpContext).GetMsalCacheInstance();
ConfidentialClientApplication cca = new ConfidentialClientApplication(AzureAdB2COptions.ClientId, AzureAdB2COptions.Authority, AzureAdB2COptions.RedirectUri, new ClientCredential(AzureAdB2COptions.ClientSecret), userTokenCache, null);
AuthenticationResult result = await cca.AcquireTokenSilentAsync(scope, cca.Users.FirstOrDefault(), AzureAdB2COptions.Authority, false);
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, AzureAdB2COptions.ApiUrl);
// Add token to the Authorization header and make the request
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = await client.SendAsync(request);
My question is, when login in to UI using your social account and accessing back-end resources with another silent request. Does it count as 1 authentication? Or it counts as 2 authentication in Azure B2C?

After your web application handles the authentication response, the ConfidentialClientApplication.AcquireTokenByAuthorizationCodeAsync method retrieves the access token from Azure AD B2C and then writes it to a token cache, which is implemented by the MSALSessionCache class.
Before your web application invokes your web API, the ConfidentialClientApplication.AcquireTokenSilentAsync method reads the existing access token from the token cache and only requests a new access token from Azure AD B2C if:
The access token doesn't exist in the token cache;
It is about to expire; or
It has expired.
If the existing access token is read from the token cache, then you aren't charged for the issued token.
If a new access token is requested from Azure AD B2C, then you are charged for the issued token.

Related

How do I authorize a service to call an API using Azure Active Directory?

I have a service that gets an access token from Azure AD. I have an API that I would like to accept that token as authorization.
My service code to call the API is
HttpClient client = new HttpClient()
{
BaseAddress = new Uri("https://localhost:44372/")
};
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, client.BaseAddress + "api/todolist");
request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", accessToken);
HttpResponseMessage response = await client.SendAsync(request);
The response I get back is a 401 - Unauthorized.
I have a feeling that the issue is in the API ConfigureServices function; specifically (this was taken from an example, so I don't really know what it means yet):
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.TokenValidationParameters.RoleClaimType = "roles";
});
I'm new to Azure and authentication in general so I don't know what options are available or appropriate. I also am not sure how to set up the applications in Azure to allow this. I have the application id of the service set up as an Authorized client application of the API; it is also listed int the knownClientApplications in the API manifest.
There are just so many knobs to turn, I have no idea where to go from here. If anyone can let me know some things to try, that would be outstanding.
Thanks
Here is a code sample on how to call a web API in an ASP.NET Core web app using Azure AD:
https://learn.microsoft.com/en-us/samples/azure-samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore/calling-a-web-api-in-an-aspnet-core-web-application-using-azure-ad/
This sample contains a web API running on ASP.NET Core 2.0 protected by Azure AD. The web API is accessed by an ASP.NET Core 2.0 web application on behalf of the signed-in user. The ASP.NET Web application uses the OpenID Connect middleware and the Active Directory Authentication Library (ADAL.NET) to obtain a JWT bearer token for the signed-in user using the OAuth 2.0 protocol. The bearer token is passed to the web API, which validates the token and authorizes the user using the JWT bearer authentication middleware.

Authenticate to Azure AD on-behalf-of a client application

In a scenario like this: https://github.com/Azure-Samples/active-directory-dotnet-webapi-onbehalfof
I want to authenticate to Azure AD in the back end on behalf of a client instead of a user. I couldn't find an appropriate example in the documentation that fits this case.
So what am I doing?
In the client:
var authContext = new AuthenticationContext(authorityUrl);
var result = authContext.AcquireTokenAsync(webServiceUri, new ClientCredential(nativeClientId, nativeClientSecret)).GetAwaiter().GetResult();
In the back end service:
var authContext = new AuthenticationContext(authorityUrl);
var result = authContext.AcquireTokenAsync(office365ResourceUri, new ClientAssertion(webClientId, result.AccessToken))
This throws the following exception:
AADSTS70002: Client assertion application identifier doesn't match 'client_id' parameter.
It only succeeds when I'm pointing the same service (refering to itself!) in the back end as from the client:
authContext.AcquireTokenAsync(webServiceUri, new ClientAssertion(nativeClientId, result.AccessToken))
But this doesn't make sense as the service has to go to an Office 365 API.
Anyone an idea?
The OAuth 2.0 On-Behalf-Of flow is to propagate the delegated user identity and permissions through the request chain. For the middle-tier service to make authenticated requests to the downstream service, it needs to secure an access token from Azure Active Directory (Azure AD), on behalf of the user.
In your scenario , you could use client credential flow to acquire token for the office 365 api in your service app , without any human interaction such as an interactive sign-on dialog .
Please click here for more details about Authentication Scenarios for Azure AD .

Authenticate App Services backend using Microsoft Graph token?

Edit:
I have added the "id_token" but still get an "Unauthorized" response.
Here is my login code:
PublicClientApplication myApp = new PublicClientApplication("My-AppID-From-App-Registration-Portal");
string[] scopes = new string[] { "User.Read" };
AuthenticationResult authenticationResult = await myApp.AcquireTokenAsync(scopes).ConfigureAwait(false);
JObject payload = new JObject();
payload["access_token"] = authenticationResult.AccessToken;
payload["id_token"] = authenticationResult.IdToken;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, payload);
Original Post:
Is it possible to authenticate to a App Services backend using the token retrieved from Microsoft Graph?
I have already tried using this token and calling LoginAsync() with AzureActiveDirectory as the provider, this doesn't work.
JObject payload = new JObject();
payload["access_token"] = GraphAuthenticationHelper.TokenForUser;
user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
Is this possible?
UPDATE: In my original answer, I said you cannot do this. But in reality, you can do this but it's a dangerous thing to do since anyone with a valid Microsoft Graph token could theoretically access your APIs. Before I walk you down that path, let me describe the "right" way to access the Microsoft Graph on behalf of your end user.
The right way to do this is to use the on-behalf-of flow in the mobile backend code to exchange the user's ID token for a Microsoft Graph token. The flow looks like the following:
Client initiates a login with AAD using MSAL and sets the resource to the mobile backend (not the Graph). The result should be a set of tokens.
Client uses the mobile SDK to do a login with BOTH the access_token AND the id_token from #1.
Example code:
JObject payload = new JObject();
payload["access_token"] = {access_token.from.msal};
payload["id_token"] = {id_token.from.msal};
var user = await MobileService.LoginAsync(
MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
payload);
The backend code exchanges the user's ID token (from the x-ms-token-aad-id-token request header) for a graph token. This token exchange is known as "on-behalf-of" and is documented here. I think this can be done using ADAL or MSAL libraries, but I wasn't able to find documentation. It's also simple enough that you could implement the HTTP protocol directly without too much trouble.
The backend uses the newly acquired MS Graph token and makes the graph API call.
You can also cache the graph token that you acquire on the backend so that each API call doesn't require more AAD API calls to do token exchange.
I think no ,please refer to document : https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-dotnet-how-to-use-client-library#a-nameauthenticationaauthenticate-users
Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
The audience of the access token should be the client ID for your mobile app backend . So if resource is https://graph.microsoft.com/(aud claim in access token) , then Client-managed authentication won't work .

Azure AD OpenIDConnect + ASP.NET Core - Authenticate and Extra Permissions/Token?

I am using the following bits against my Azure AD to authenticate with ASP.NET Core.
https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapp-openidconnect-aspnetcore/
https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore
I have the basic login/auth working after creating an Azure AD app. User can login/logout.
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
Also, is it possible to retrieve a bearer token via this approach? Or does this require another type of call or extending "scope"? So that for example I could retrieve the authenticated users Manager.
https://graph.microsoft.com/v1.0/me/manager
My question is given this, what's the best way when a user Auth's to log to a DB? I thought about making the redirect URL to an endpoint, saving, then just redirecting back to "Home" but is that ideal?
This way only able to log those who already sign-in your app successfully. It is not able to log those users who are attempt to sign-in your app but enter the wrong password.
Azure AD already provide lots of report to gain visibility into the integrity and security of your organization’s directory.( refer here)
And if you are using the Azure AD Premium, you can review the sign-in activities via the Azure new portal below:
And if you want to store the sign-in activity in your web app, you can write the custom code after the token is verified. Here is the code for your reference:
// Configure the OWIN pipeline to use OpenID Connect auth.
app.UseOpenIdConnectAuthentication(new OpenIdConnectOptions
{
ClientId = Configuration["AzureAD:ClientId"],
Authority = String.Format(Configuration["AzureAd:AadInstance"], Configuration["AzureAd:Tenant"]),
ResponseType = OpenIdConnectResponseType.IdToken,
PostLogoutRedirectUri = Configuration["AzureAd:PostLogoutRedirectUri"],
Events = new OpenIdConnectEvents
{
OnRemoteFailure = OnAuthenticationFailed,
OnTokenValidated = context => {
//write the custom code to store users login-in
return Task.FromResult(0); }
},
});
Also, is it possible to retrieve a bearer token via this approach?
Yes. We can get the token after receive the authorization code. You can refer the code sample here to acquire the token from asp.net core app.

Custom authorization with Azure AD Authentication in OWIN Web API

We are using Azure AD authentication for one of our client application. We want to implement claims based authorization along with it.
Our application set up is Angular Based client app connecting with Web API (both client server secured using Azure AD Bearer Authentication). Server application is hosted using OWIN.
We need to provide custom authorization on server side. There is a provision in Azure AD for adding users and roles. However, that is not enough for us. Our user management is through AD & Security Groups. To gain access to application, users need to part of a base group and further rights (access particular section of application, edit a specific entity etc.) are assigned based on additional groups or given directly to users in the application. Essentially, not all users will be registered in the application and we may have to query the AD using graph API to check which all application specific groups they belong.
OWIN authentication and authorization model is based on Authentication Server and Resource server. We can separate them on need basis. However, in our case, we need to split the authentication and authorization. When the client presents the bearer token, we need to verify if the token is valid and then add claims to user profile. We also need to cache the user claims so that we do not hit the database frequently. (Our client app make multiple Web API calls in one user action.)
What is the location in Identity 2.0 where
I can verify the token &
insert application specific claims
If my entire application revolves around the user authorization and all queries need to be filtered on what data the user can access, which is a more suitable design pattern for the Web API application?
I believe what you're looking for are the Authentication and Authorization filters in the ASP.NET Web API 2.0 stack.
You can implement per-web method authorization by implementing System.Web.Http.Filters.IAuthorizationFilter on an attribute class, then decorate the web action methods of your service controller with that attribute. Web API 2.0 will select a method based on URL routing, notice that there is an attribute on that method implementing IAuthorizationFilter, and will call the ExecuteAuthorizationFilterAsync method on that attribute instance before calling the web method. Placing the authorization step before the web method invocation allows invalid requests to be discarded quickly, before getting into the heavy lifting of parameter binding.
The incoming token is validated by an IAuthenticationFilter implementation which executes before the authorization step.
Documentation and examples are extremely hard to find. Here's one of the few search results that are actually relevant: http://thegrumpycoder.com/post/105427070626/secure-web-services-with-web-api-and-sitecore
you can check if this helps...
UserProfile profile = new UserProfile(); //To deserialize the response stream (JSON)
string tenantId = ClaimsPrincipal.Current.FindFirst(TenantIdClaimType).Value;
AuthenticationResult result = null;
try
{
// Get the access token from the cache
string userObjectID =
ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")
.Value;
AuthenticationContext authContext = new AuthenticationContext(Startup.Authority, new NaiveSessionCache(userObjectID));
//use ClientID, ClientSecret
ClientCredential credential = new ClientCredential("b557ceed-xxxx-xxxx-xxxx-xxxxxxxbc240", "AXFxx//xxxxxxxxxxxxxjVFz4sqYm8NDAPEOLkU=");
result = authContext.AcquireTokenSilent("https://graph.windows.net", credential,
new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
// AcquireTokenSilent may throw exception if the cache is empty. In that case, logout the user and make him login.
string requestUrl = String.Format(
CultureInfo.InvariantCulture,
"https://graph.windows.net/cdmsdev.onmicrosoft.com/groups/b40xxxx-14a8-xxxx-9559-xxxxxxca90c8/members/?api-version=1.6");
//Above grap API url is for getting list of users who belong to a specific group (with GUID b40xxxx-1....)
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
HttpResponseMessage response = client.SendAsync(request).Result;
if (response.IsSuccessStatusCode)
{
var upn = ClaimsPrincipal.Current.Identity.Name;
string responseString = response.Content.ReadAsStringAsync().Result;
profile = JsonConvert.DeserializeObject<UserProfile>(responseString);
if (profile.Users.Contains(upn)) //check if the current user is in the list of users of the Admin group
return true;
}
}
catch (Exception e)
{
//handle authorization exception here
}
The graph API URL can be replaced with a function to check for membership of a specific group which will directly return a bool value instead of getting all users of that group.

Resources