How to customize SAML token claims to include custom attribute created as Directory Schema Extension in Azure AD

Background

Recently I was engaged to assist a customer with an interesting issue. This customer follows the guideline at https://docs.microsoft.com/en-us/previous-versions/azure/ad/graph/howto/azure-ad-graph-api-directory-schema-extensions to create a custom User attribute as a Directory schema extension and then assigns a value to the attribute. As an example, I have a few custom attributes created this way as can be seen by Microsoft Graph Explorer:

The customer wants to have this custom attribute returned as a claim in a SAML token when using an Enterprise Application to sign users in.

Note: By default, Azure AD only returns the claim if its value is not null.

Resolution

After spending some time researching, the only way to achieve this is by using claims mapping policy as detailed below. It’s important to note that custom attributes created this way do not show up in the Enterprise Application’s Single Sign-on User Attributes & claims customization wizard.

  1. Run the following Azure AD Powershell (must use AzureAD-Preview module) command to log in with an organizational admin account

    Connect-AzureAD

  2. Create a new claims mapping policy similar to the following. In this example, I want to include the customer attribute ‘extension_a5c236591f3f48b39a63d962d7364ad2_ClaimsxrayAttrib’ in my SAML token:

    New-AzureADPolicy -Definition @(‘{“ClaimsMappingPolicy”:{“Version”:1,”IncludeBasicClaimSet”:”true”, “ClaimsSchema”: [{“Source”:”user”,”ExtensionID”:”extension_a5c236591f3f48b39a63d962d7364ad2_ClaimsxrayAttrib”,”SamlClaimType”:”http://schemas.xmlsoap.org/ws/2005/05/identity/claims/extension_a5c236591f3f48b39a63d962d7364ad2_ClaimsxrayAttrib”}]}}’) -DisplayName “ClaimsxrayPolicy3” -Type “ClaimsMappingPolicy

  3. Assign the policy to the Enterprise Application:

    Add-AzureADServicePrincipalPolicy -Id <Enterprise App objectID> -RefObjectId <Policy ID>

    To find the Enterprise App Object ID, look at the Object ID field from the ‘Properties’ blade for the Enterprise Application:

If configured properly you should see desired claim in the AttributeStatement section of the SAML token:

<AttributeStatement>
…
     <Attribute  Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/extension_a5c236591f3f48b39a63d962d7364ad2_ClaimsxrayAttrib">
	<AttributeValue>My Cool claims</AttributeValue>
     </Attribute>
</AttributeStatement>

Troubleshooting Asp.Net OWIN and Asp.Net Core Authentication sign-in failures with Azure Active Directory

Let get started!

This article assumes you are using your own code to perform the authentication to Azure Active Directory.

IMPORTANT: So if your using Azure App Services or Azure Function Apps Authentication/Authorization feature, this article is not for you.

You are developing a Asp.Net OWIN or Asp.Net Core Authentication web application and integrating it with Azure Active Directory. You run into some issues during the sign-in process with no error message or any hint on what the problem might be. Some of the behaviors that you might see are…

  • The dreaded infinite loop between your web app and Azure Active Directory.
  • After signing into Azure Active Directory, you land back on to your Web Application like it never signed in.
  • You land on your error page with no useful error message.

The purpose of this article is not to show you how to resolve the failed sign-in attempt. Rather, I want to show you how to troubleshoot and maybe expose the hidden error message. Once you have unraveled the hidden error message, hopefully that will lead you down a path to resolve the failed sign-in attempt.


This is where the OnAuthenticationFailed notification comes in handy…

In Asp.Net, you want to make sure your code looks something like this for the for the AuthenticationFailed event notification event notification (in startup.auth.cs)…
[gist id=”5773aac85537d35ca0f0d496d1e7b581″ file=”AspNet_OWIN_OnAuthenticationFailed.cs”]


In Asp.Net Core, you want to make sure your code looks something like this for the for the OnAuthenticationFailed event notification event notification (in startup.cs)…
[gist id=”813dd19091dfa8650895182cb45d5d1c” file=”AspNetCore_Auth_OnAuthenticationFailed.cs”]


You can of course tweak this so that you can either send the error message to your logs or send it to a custom error page.

At minimum, we should see the error message in the address bar…

or in the case of the infinite loop, see it in the Fiddler capture…


For more information about using Fiddler, check out our Blog post here…

For a list of Azure Active Directory errors and and some tips on how to resolve, check out the following article…
https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes

Walkthrough: MSAL.Net client calling Azure API App with Easy Auth enabled in a B2C tenant

In a previous blog post, I talked about how to use MSAL.Net client application to call an Azure Function App with Easy Auth enabled in a regular tenant. In this post, I’ll describe the process to use an MSAL.Net client application to call a Web API application with Easy Auth enabled in a B2C tenant. I assume you already have an API App created on Azure App Service. If you don’t have one, follow the instruction at https://docs.microsoft.com/en-us/azure/app-service/app-service-web-tutorial-rest-api to create a sample API app and deploy to Azure App Service. Let’s go through this exercise:

  1. Register the API App in a B2C tenant

Log into your Azure AD B2C tenant -> Click on the ‘Azure AD B2C’ blade -> Applications -> Add to add a new application with the following setting:

Name Enter a name
Include web app / web API yes
Allow implicit flow yes
Reply URL https://<sitename>.azurewebsites.net/.auth/login/aad/callback
App ID URI <tenant>.onmicrosoft.com/demo or enter your own scope
Native client no

Note:
for the Reply URL, you will need to enter the URL in that format since the aad/callback endpoint gets processed by the Easy Auth module

Here is what mine looks like after this is done. My API App is hosted at https://bhwebapitest.azurewebsites.net. Take note of the Application ID as you will need it later to configure Easy Auth

Click on ‘Published scopes’ and take note of the full scope value since you will need this for the MSAL.Net application.

  1. Register an MSAL.Net client app in a B2C tenant

Follow the step above to create a new app registration with the following setting:

Name Enter the name of the client app
Include web app / web API No
Include native client yes

Once the application is created, click on API access -> Add -> select the API app created above in step 1 and select all available scopes and click on OK.

Here is what mine looks like after this is done. Take Note of the Application ID in the Properties blade as you will need it for the MSAL.Net application

  1. Create a new User flow policy in the B2C tenant

In the Azure AD B2C blade -> select User flows (policies) -> click on “New user flow” and create a new “Sign up and sign in” policy. Follow the wizard to provide a policy name and select any attribute you desire under “User attributes and claims” section.

Once the policy is created, select that policy and click on “Run user flow” button. Take note of the URL under “Run user flow” as you will need it to configure Easy Auth.

My policy in this case in this case is B2C_1_SiUpIn and the well-known configuration URL is https://hellob2c.b2clogin.com/tfp/hellob2c.onmicrosoft.com/B2C_1_SiUpIn/v2.0/.well-known/openid-configuration

Note that this URL is dependent upon the domain used in the “Select domain” drop down. I used the format b2clogin.com domain in this case. If you change this domain to login.microsoftonline.com, the well-known configuration URL will be changed accordingly to https://login.microsoftonline.com/tfp/hellob2c.onmicrosoft.com/B2C_1_SiUpIn/v2.0/.well-known/openid-configuration.

  1. Enable Easy Auth for the API Application

Follow the steps in the previous blog to turn on Easy Auth. Choose Advanced Mode instead of Express Mode for this. For ‘Client ID’ field enter the Application ID of the B2C App created in step 1 above and use the well-known URL in the “Run user flow” for the “Issuer Url” field (see screen shot below). Click OK and Save to save the setting. Test the Easy Auth configuration in the web browser by browsing to your web API URL to make sure it’s working correctly. You should be prompted for login. Use an account in the B2C tenant to verify you can log in and get to the API application.

  1. Create an MSAL.Net application

Create a C# Console Application with the following code. Make sure to add the MSAL.Net nuget package (Microsoft.Idenity.Client) to the application. You will need to change the following parameter for your application

// your b2c tenant name
private static readonly string Tenant = "<tenant>.onmicrosoft.com";
// your b2c tenant name
private static readonly string AzureAdB2CHostname = "<tenant>.b2clogin.com";
// Application ID of the MSAL.Net app
private static readonly string ClientId = "<Application ID>";
// Your Policy Name. Should be something similar to B2C_1_SomeName
public static string PolicySignUpSignIn = "<Your Policy Name>";
// use the full API scope in the "Published scopes" of the API app
public static string[] ApiScopes = { "<Your Web API scope>" };
// Your Web API
public static string ApiEndpoint = "<Your API endpoint>";

[gist id=”4e9f87996c88d65f26f247b381ea6505″ file=”MSALNetB2C.cs”]
Now you can test your MSAL.Net application to verify that it can successfully call the Web API application.

If you are using login.microsoftonline.com domain…

Just in case you are using login.microsoftonline.com domain instead of the b2clogin.com domain, in the Easy Auth ‘Issuer Url’ configuration you will need to enter the well-known configuration URL for login.microsoftonline.com as seen in the Azure AD B2C portal.

For the MSAL.Net application everything else is the same as above except for the AzureAdB2CHostname variable and it should be “login.microsoftonline.com”

private static readonly string AzureAdB2CHostname = “login.microsoftonline.com”;

References

https://cgillum.tech/2016/08/10/app-service-auth-and-azure-ad-b2c-part-2/

Receiving error AADSTS7000218: The request body must contain the following parameter: ‘client_assertion’ or ‘client_secret’

Problem:

An application receives the following error when authenticating to Azure Active Directory:

{
     “error”: “invalid_client”,

     “error_description”: “AADSTS7000218: The request body must contain the following parameter: ‘client_assertion’ or ‘client_secret’.\r\nTrace ID: xxx\r\nCorrelation ID: xxx\r\nTimestamp: 2019-08-18 20:38:28Z”,

     “error_codes”: [7000218],

     …

}

What does this error mean?

The error is what it said.  When authenticating to Azure AD to get an access token, the client application is not providing its “password” (in the form of either a client secret or a client assertion) as expected by Azure AD’s token endpoint.  Before diving into this error, let’s spend a few minutes understanding the context of the issue.  As defined in the OAuth2 specifications, there are 2 types of client applications:

  • Confidential Client – a client who is capable of storing a secret (used to authenticate to Azure AD).  An example of this client is a web application, where its code and secret are stored on the server that’s not exposed to the public.  The application’s confidential information can only be accessed by an admin person.
  • Public Client – as you guess,  a client not capable of storing any secret.  An example of a public client is a mobile application or a desktop application running at public kiosk in an insecure and unmanaged environment.

A confidential client is always expected to provide its credential (client secret or assertion) when authenticating to Azure AD.

So how does Azure AD know if the authenticating client is public or confidential?

In most cases, Azure AD looks at the reply / redirect URL provided in the request and cross check it with the reply URL registered in Azure AD’s App Registration blade find out the type (see screen shot below).  A ‘Web’ type is Confidential client and the ‘Public client (mobile & desktop)’ type is a Public client

In certain OAuth2 authentication flows such as OAuth2 resource owner password credentials (ROPC) grant flow, OAuth2 device code flow, and Integrated Windows Authentication, there is no reply URL provided in the token request.  In such case,  Azure AD looks at the app registration’s default type (see screen shot below) to determine if the client is confidential or public.

If “yes’ is selected, it’s a public client.  ‘No’ is a confidential client.

Yes in the current Azure AD’s App Registration model, a registered application can be both a public client and a confidential client, depending on the context the application is used in.  This is because an application may have part of it used as a public client while some other parts are designed to be used as a confidential client.  Depending on certain workflows the application developer must decide if the application should act as public or confidential client.

A Confidential client is expected in certain OAuth2 grant flows

Client Credentials flow, Authorization Code flow, and On-Behalf-Of flow are used by Confidential client to request a token.  Azure AD will return the above error if the request is missing a client secret or a client assertion.

How do I know which grant type and what redirect URL (if any) my application is using?

Review your application code or take a Fiddler trace to see what the ‘grant_type’ parameter and what ‘redirect_uri’ parameter are sent in the POST request to Azure AD’s token end point: https://login.microsoftonline.com/<tenant name>/oauth2/token (V1 endpoint) or https://login.microsoftonline.com/<tenant name>/oauth2/v2.0/token (V2 endpoint).

Refer to the table below for more information on grant type and oauth2 flow:

OAuth2 flow

grant_type value

ROPC

password

Device code

urn:ietf:params:oauth:grant-type:device_code

Authorization Code

authorization_code

Client Credentials

client_credentials

On-Behalf-Of

urn:ietf:params:oauth:grant-type:jwt-bearer

SAML Bearer Assertion

urn:ietf:params:oauth:grant-type:saml1_1-bearer

So what’s the resolution?

By now you probably figure this out that if the application is expected to be a Confidential client then provide the client secret or assertion.  In some authentication flow scenario like ROPC or Device Code flow where you don’t expect the client application to be confidential, follow the steps below to change the default type to Public client.

  1. Go to the App Registration for this application
  2. Select “Authentication” – > tick ‘Yes’ for Default client type -> Save the change

Reference

https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-Applications

AADSTS5011 Error on Kubernetes container app running .Net Core code with an Ingress Controller

I recently worked with a customer who was receiving an AADSTS5011: The reply url specified in the request does not match the reply urls configured for the application on his .Net Core Kubernetes container application with an Ingress Controller. When running from the Docker environment, there was no issue however, the redirect URI was configured for http://localhost:someport . Once deployed, the redirect URI ( as seen in the address bar of the browser at the Azure login prompt ) was set to his application properly however, the context was HTTP and not HTTPS. This is a problem because, in Azure AD, you have to set the reply URL scheme to be https, except for localhost. The customer was using Microsoft.Identity.Web for authentication.

SSL Was being terminated at the controller

The first issue we discovered was that the controller was terminating SSL. We worked with a Kubernetes engineer to get the controller setup for SSL passthrough, setting up a SSL certificate for the application and still, the context was set to HTTP instead of HTTPS and now, the customer was experiencing an infinite number of redirects until it just timed out ( the new error was “…too many redirects” ).

Mircrosoft.Identity.Web not fully ready for GA

Even though the customer found the Microsoft.Identity.Web library on our GitHub, this library isn’t fully ready for GA and may not be anytime soon ( a fact that I hope has been noted on the GitHub by now ). However, we did finally resolve this last issue by simply specifying the Context.Request.Scheme to be “https” in code. This was presented from an ASP.NetCore engineer with this document: https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/proxy-load-balancer?view=aspnetcore-2.1#forwarded-headers

Here is the code snippet where the final fix was done in the applications startup.cs file:

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)

{

    app.Use((context, next) =>

    {

        context.Request.Scheme = “https”;

        return next();

    });

    app.UseForwardedHeaders();

Conclusion

Even though the Microsoft.Identity.Web library isn’t fully GA ready yet and we experienced serious difficulties in the Kubernetes environment, a multi-faceted resolution was found for the problem. Hopefully, this blog post makes the experience less painful for the next developer!

Walkthrough: Using MSAL.Net to call an Azure Function App with Easy Auth enabled

In this post, I’ll walk through the steps for how to create an application using Microsoft Authentication Library for .Net (MSAL.Net) to call an Azure AD protected Azure Function App using Easy Auth (Azure App Service’ Authentication and Authorization feature).

1. Create an Azure Function with Easy Auth enabled:

Assuming you already have an Azure Function App created (refer to https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-function-app-portal if you don’t know how to create one), go to that Function App in the Azure portal and create an Azure Function. For convenience, I’ll use the Http Trigger In-portal template to create an Azure Function.

Click on ‘Continue’ and then select ‘Webhook + API’ blade and then ‘Create’.

The template generates the following code in the run.csx file

#r "Newtonsoft.Json"

using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;

public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");

string name = req.Query["name"];

string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;

return name != null
? (ActionResult)new OkObjectResult($"Hello, {name}")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}

Now that we have our Function created, we need to turn on Easy Auth, so navigate to the Azure Function App and select the “Authentication / Authorization” link in the ‘Platform features’ tab

 

Select ‘On’ for App Service Authentication and ‘Log in with Azure Active Directory’ in the “Action to take when request is not authenticated” section

 

 

In the ‘Authentication Providers’ section select ‘Azure Active Directory’ and choose the Express for Management mode and ‘Create New AD App’ and Save:

Now that Easy Auth is turned on, test the Function App URL in the browser to make sure it requires authentication. The Function URL can be obtained from the “</> Get function URL” link. Append the name query string at the end since the Function expects this name value. My test Function URL is https://easyauthfunctest.azurewebsites.net/api/HttpTrigger1?code=zIrQXHC9ypU2ewa4YadZfXgka7XgNG/U7J/kDGW79aXig3q907jo2A==&name=Azure

 

When I navigate to the above link, I get prompted for credential and then see the message “Hello, Azure” in the browser after logging in.

 

 

2. Create a new App Registration for the MSAL .Net app

Navigate to the Azure portal -> Azure Active Directory -> App registrations -> New registration to create a new App Registration. I use the following parameter for my registration.

Name

msalclient

Supported account types

Accounts in this organizational directory only

Redirect URI

myapp://auth

Once created, go to that app registration select API permissions -> Add a permission -> select the above Azure Function App in the ‘APIs my organization uses’ tab to add that permission:

 

Take note of the following since you will need them later when creating the MSAL App.

 

Overview blade:

  • Application ID
  • Directory ID

Authentication blade:

  • Redirect URI – this should be myapp://auth
    as registered above

API permissions blade:

  • Get the scope for this Azure Function App: (see screen shot below):

 

3. Create an MSAL Application

  1. In Visual Studio, create a new C# Console (.Net Framework) project
  2. Add the “Microsoft.Identity.Client” nuget package to the project

  3. Use the following code in the Program.cs file. Remember to fill in the scopes, ClientId, replyURL, and Tenant (Directory ID) with the info above

 

class Program
{
static string[] scopes = new[] { "https://easyauthfunctest.azurewebsites.net/user_impersonation" };
static string ClientId = "xxx";
static string Tenant = "xxx";
static string replyURL = "myapp://auth";
…
}

[gist id=”9f03df153b77e7cf78041f3fba4880f5″ file=”Program.cs”]

  1. Build and run the application to login to Azure AD. Once logged in you should see the following output in the console

Voila, we got the output from Azure Function App.

Understanding Azure AD’s On-Behalf-Of flow (aka OBO flow)

Background

Microsoft Azure Active Directory supports an OAuth2 protocol extension called On-Behalf-Of flow (OBO flow). This is documented at both the Microsoft Identity Platform V1 and V2 endpoint. The OBO flow is used in the following scenario. Both Web API 1 and Web API 2 are protected by Azure AD.

  1. A client application (could be a SPA app, a front-end Web Application, or a native application) signs a user into Azure AD and request a delegated access token for Web API 1
  2. Client application then calls Web API 1 with the issued access token
  3. Web API 1 in turn needs to call a downstream Web API 2 so it uses its access token (in step 2 above) to request an access token for Web API 2. What happens in this step is that Web API 1 uses the OBO flow to exchange its access token for another resource’s access token. The exchanged token is still issued on behalf of the original sign in user and it has delegated permission.
  4. Web API 1 uses the new access token to call Web API 2

Let’s look at the parameters used in an OBO flow at the V1 endpoint below. I want to call out a few highlighted parameters as their significance will become more obvious a little bit later.

assertion:
this is the access token issued in step 2 above

client_id:
application id of Web API 1

Resource:
this is Application ID URI or Application ID of Web API 2

Let’s look at an OBO end to end traffic in Fiddler:

Frame 1 – 14 below shows the user navigates to the web site and is redirected to Azure AD to log in. Frame 15 is the request to the token endpoint to get an access token for Web API 1

Hover over image to enlarge

In this example Web API 2 is Microsoft Graph. In frame 19 below Web API 1 uses an OBO flow to request a token for Microsoft Graph. It uses the access token received in frame 15 as the assertion parameter.

Hover over image to enlarge

It is extremely important to use the correct parameter in the OBO flow. Note that the OBO parameters client_id and the assertion (access token) are for the calling application (Web API 1) in this token exchange request.

Common pitfall customers run into when using the OBO flow

I have seen a few AADSTS error returned for this flow when either the client_id or the assertion parameters used are not for the calling application (Web API 1). Below are a few examples:

HTTP 400 error: AADSTS500131: Assertion audience does not match the Client app presenting the assertion. The audience in the assertion was ‘00000002-0000-0000-c000-000000000000’ and the expected audience is …

Root cause: The access token used in the assertion is for a different application / resource instead of for the calling app Web API 1. The GUID in this error is an Azure AD Graph resource.

HTTP 400 error: AADSTS50013: Assertion failed signature validation. [Reason – The provided signature value did not match the expected signature value., Thumbprint of key used by client:…

Root cause: The access token used in the assertion is for Microsoft Graph resource (https://graph.microsoft.com)

HTTP 400 error: AADSTS50013: Assertion failed signature validation. [Reason – The key was not found., Thumbprint of key used by client: ‘B25930C…..
Root cause: Web API 1 is a SAML Application (check the Enterprise Application blade to see if Single sign-on is enabled and there is a SAML signing Certificate attached).

HTTP 500 error: AADSTS50000: There was an error issuing a token.

Root cause: the client id used is either not valid or does not exist in the tenant.

How can I diagnose this issue?

  • Take a Fiddler trace to see what the parameters used are.
  • Use https://jwt.ms to decode the access token assertion and look at the “aud” (audience) claim to see if it’s for the calling web API 1

What if my Web API 2 is SAML Application?

If the downstream API App can only consume SAML token (instead of jwt token), you can certainly use the OBO flow to exchange a JWT token for the SAML token using the following parameters (see https://docs.microsoft.com/en-us/azure/active-directory/develop/v1-oauth2-on-behalf-of-flow for more info). The key is in the parameter requested_token_type. The allowed values are

urn:ietf:params:oauth:token-type:saml2 – SAML 2.0 token

urn:ietf:params:oauth:token-type:saml1 – SAML 1.1 token

Note:
At the time of this writing, this JWT – SAML token exchange OBO flow is only available in the V1 endpoint.

Note: Azure AD returns the SAML token encoded in UTF8 and Base64URL as noted in the documentation

What about Azure AD B2C?

The OBO flow is currently not supported in Azure AD B2C per Application types that can be used in Active Directory B2C

Drop me a comment if you find this useful or any other important information I should add.

==========

Update 8/22 – added one more condition for error AADSTS50013 due to SAML App

Common Microsoft First Party Apps resource table

The information below is taken from the manifest file in the App Registration portal

Azure Resource

Resource URL

Resource App ID

Azure AD Graph https://graph.windows.net/ 00000002-0000-0000-c000-000000000000
Microsoft Graph https://graph.microsoft.com/  00000003-0000-0000-c000-000000000000
Azure Key Vault https://vault.azure.net cfa8b339-82a2-471a-a3c9-0fc0be7a4093
Azure Service Management https://management.azure.com/ 797f4846-ba00-4fd7-ba43-dac1f8f63013
Power BI Service https://analysis.windows.net/powerbi/api/ 00000009-0000-0000-c000-000000000000
Azure Storage https://storage.azure.com/ e406a681-f3d4-42a8-90b6-c2b029497af1
Azure DevOps https://app.vssps.visualstudio.com/ 499b84ac-1321-427f-aa17-267ca6975798
Office 365 Management API https://manage.office.com/ c5393580-f805-4401-95e8-94b7a6ef2fc2
Intune https://api.manage.microsoft.com/ c161e42e-d4df-4a3d-9b42-e7a3c31f59d4
SharePoint https://microsoft.sharepoint-df.com/ 00000003-0000-0ff1-ce00-000000000000
Exchange https://outlook.office365.com/ 00000002-0000-0ff1-ce00-000000000000
Azure SQL Database https://database.windows.net/
Microsoft.ServiceBus https://servicebus.azure.net 80a10ef9-8168-493d-abf9-3297c4ef6e3c

Note:
for Azure Key Vault there is no trailing slash in the Resource URL

References:

Authenticate with Azure Active Directory from an application for access to blobs and queues

Infinite redirect between OpenID Connect Application and Azure AD

Recently I came across an interesting infinite redirection problem between an OpenID Connect (OIDC) Application and Azure AD as demonstrated in the Fiddler screen shot below. After authentication to Azure AD, we are stuck in an infinite loop between the web site and Azure AD. Eventually the browser gives up and throws an error. This problem only occurs if I start browsing to the site by http protocol (frame 3). If I use https, everything works fine.

Looking at the above Fiddler trace, what I notice unusual about this trace is that the problem starts at frame 16. The response in this frame should be a 200 OK instead of a 302 redirect back to Azure AD. This is the starting point for the infinite loop.

Let’s dive into the analysis

Looking at frame 15 below, we POST the id token to the site. After validating the token is good, the application invalidates the nonce cookie by setting its ‘expires’ attribute to a time in the past – Jan 1970 (expected). The application then sets up an authenticated session with the client by setting the “.AspNet.Cookies” cookie. Note there is a ‘secure‘ attribute on this cookie, which means that the cookie is only sent for secure https request. The POST request results in an HTTP redirect back to the site. See the problem? The redirection happens over http because this is the original scheme used to browse to the site (frame 3).

Hover over image to enlarge

In frame 16 the browser sends an http request back to the site. This request lacks the authentication “.AspNet.Cookies” cookie (remember the secure attribute?) therefore it is redirected back to Azure AD for log in, and we keep repeating the same sequence over and over again leading to the infinite loop.

Hover over image to enlarge

Doing some research shows that this problem was reported widely by multiple people in the following forum:

https://stackoverflow.com/questions/31588552/owin-and-azure-ad-https-to-http-redirect-loop

https://github.com/aspnet/Security/issues/219

What’s the solution?

The ideal way to resolve this issue is to force https navigation to the site. It’s always a good idea to use https scheme when browsing to web sites that require authentication. There are multiple ways to force https navigation mentioned in the above links. If your scenario requires the initial navigation to happen over http, you can customize the Cookies Authentication middleware to allow the authentication AspNet cookie for both http and https scheme by setting the CookieSecure attribute to CookieSecureOption.Never as followed in the Startup.Auth.cs file:

public void ConfigureAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                CookieSecure = CookieSecureOption.Never
            });

            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = authority,
…
}

Retrieving user information from Azure AD’s UserInfo Endpoints

In OpenId Connect (OIDC) we have the UserInfo endpoint, that’s specifically for the OIDC protocol and we cannot use with OAuth2 protocol.

To use this endpoint in Azure AD we need a token, and without specifying the “Resource” parameter.

How to obtain a token (V1)

For the sake of this example we’ll use the auth code grant flow to request tokens, using Microsoft Identity Platform V1 endpoint.

In a browser we can request a token like the samples below.

  1. Request Code – without specifying the “Resource” parameter, like:
    https://login.microsoftonline.com/{tenant}/oauth2/authorize?client_id={appid}&scope=openid%20offline_access%20profile&redirect_uri={redirect_uri}&response_type=code

    Response:
    https://localhost:44555/?code=AQABAAIAAAAP0wLlqdLVToOpA4kwzSnx6BjceMnEMcvAOdHgnHs3FJWDCqJbpVc5kQWtNUD8X-bUSCT1iiYd_vmY-G6nA2RPJK7C1GnSclx3UmUvIQWrANdZfoEZGY_sfTOvZ5Y6S3FVZExIue-vj89JAcJ40nMRBN…
  2. Request Token – using Code returned request Access Token again, without specifying the “resource” parameter, request to token endpoint should look like:
This image has an empty alt attribute

Response has an Access Token, that cannot be decoded:

Call the endpoint

Do the request to UserInfo endpoint (https://login.microsoftonline.com/common/openid/userinfo) using the access token, like you can see below:

Response:

Call UserInfo with Microsoft Identity Platform V2 endpoint

Above we described how to call the endpoint using the V1 endpoint, the same can be achieved using the V2 endpoints also.

When using this authentication endpoints we’ll need to get a token with the MS Graph scope and call the UserInfo endpoint – https://graph.microsoft.com/oidc/userinfo.

How to get UserInfo details from the JWT Token

If you are requesting a id_token to begin with, you can just decode it and save yourself an additional call in your application.

Below we can see a decoded id_token and information that it has.

Existing endpoints

The information of the UserInfo endpoint that you should use depends on the authentication endpoints version. And the best way to get that info is to call the metadata endpoints.

  Metadata UserInfo
V1 https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration https://login.microsoftonline.com/common/openid/userinfo (Azure AD UserInfo)
V2 https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration https://graph.microsoft.com/oidc/userinfo
(MS Graph UserInfo)

Reference

Microsoft identity platform UserInfo endpoint