Understanding the difference between application and delegated permissions from OAuth2 Authentication Flows perspective

It’s well documented in the Permissions and consent docs and the Developer Glossary page that there are 2 types of permissions for an access token: delegated permission and application permission. In simpler terms, delegated permission is the permission granted to a signed in user while application permission is the permission granted to an application. The main difference between the two is that the former requires a user to sign in while in the latter, there is no user and the application authenticates to Azure AD using its own application identity (client id and secret/assertion)

Regardless of permission type, these API permission will have to be configured in In Azure AD’s App Registration portal under API permissions blade:

Note: When configuring Application Permission, admin consent will also need to be granted for the permission to work.

Authentication Flow:

Azure Active Directory supports different OAuth2 authentication flows. The kind of authentication flow an application uses will result in a particular types of permission in an access token.

Application permission token can only be obtained from the following flow:

Delegated permission token can only be obtained from the following flow:

I have an access token. How can I tell if this token is delegated permission or application permission?

Use https://jwt.ms to look at the token claims.

For application permission token, the permissions are in the “roles” claim:

Note:  the “scp” claim is not available for application permission

For delegated permission token, the permissions are in the “scp” claim instead

Note:  the “roles” claim still may be present in the delegated permission and they contain the roles the user is assigned to in the API app.

It’s important to understand what permission type the API supports

I have seen a plethora of 400s, 401s, 403s, and 500s error due to the application calling different APIs (Azure AD Graph, Microsoft Graph, PowerBI, etc…) with the wrong permission type token. For an API, different REST end point operation may require different permission type. Always refer to the documentation for the API end point to see what permission type is supported. You will also need to assess if the authentication flow used will yield the desired permission type token. Below are a couple of example:

  1. List trustFrameworkPolicies – https://docs.microsoft.com/en-us/graph/api/trustframework-list-trustframeworkpolicies?view=graph-rest-beta&tabs=http. Calling this REST end point requires a delegated permission token. Application permission token will not work.

  2. Power BI API – although Power BI supports both delegated and application permission, application permission token is very limited in what it can do. Common task like viewing Power BI reports (which would require Report.Read.All permission) can only be done with a delegated token. In the App Registration portal, Power BI Application permission only supports the following 2 permission: Tenant.Read.All and Tenant.ReadWrite.All

While its counter part delegated permission is a much richer feature set:

What about Microsoft Graph Explorer?

I have often seen customers running into issues with calling Microsoft Graph REST endpoint. They try the Graph request in MS Graph Explorer and it works while the same request fails in their application. There are a few things one can look into:

  • MS Graph Explorer will always use delegated permission token. Check to see what permission type the token has
  • Is the same user account used to log into both MS Graph Explorer and the application?
  • Does the endpoint support delegated permission, application permission, or both?
  • Does the token have the correct permission to call the end point?

I hope this post is helpful in helping you diagnose the problem better

Receiving error IDX21323 or DX10311 RequireNonce…

Depending on what version of OWIN is used, you may see the following error in an MVC application using OpenID Connect middleware

IDX21323: RequireNonce is ‘[PII is hidden by default. Set the ‘ShowPII’ flag in IdentityModelEventSource.cs to true to reveal it.]’. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don’t need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to ‘false’. Note if a ‘nonce’ is found it will be evaluated.

Or

Error: “IDX10311: RequireNonce is ‘true’ (default) but validationContext.Nonce is null. A nonce cannot be validated. If you don’t need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to ‘false’.”

Asp.net OpenID Connect (OIDC) middleware uses the nonce cookie to prevent security replay attack. As the error said, the application throws the above exception when it does not see the nonce cookie in the authenticated request. Cookies are domain-based so once they are set for a particular domain all subsequent requests to that domain will contain these cookies as long as they are still valid (haven’t expired or deleted yet).

Before diving into the detail it’s important to understand how these cookies are set and used in a working flow from the following Fiddler trace:

In frame 116 the browser sends a request to the OIDC application protected by Azure AD. The application, upon checking sees that this request is not authenticated so it redirects the request to Azure AD (login.microsoftonline.com) for login. Note that the application also sets the red-circled OpenIdConnect.nonce cookie in the 302 redirect response.

hover over image to enlarge

After successful authentication (frame 120 – 228), Azure AD redirects the request back to the web application (frame 229) with the authenticated id token. The nonce cookie previously set for this domain is also included in the POST request. The OIDC middleware validates the authenticated token and the nonce cookie before it continues loading the page (via another redirect). Note that at this point the purpose of the nonce cookie is complete so it’s invalidated by the application setting the expiration attribute to expire (highlighted).

hover over image to enlarge

How does the above error occur?

There might be multiple reason. Below are a few different scenarios where this error happens.

Multiple Domains are used for the same web site:

The browser originally navigates to the web application on domain A (frame 9 below). The nonce cookie is then set for this domain and then later Azure AD sends the authenticated token to domain B (frame 91). The redirection to domain B doesn’t have any nonce cookie so the web application throws the above error.

hover over image to enlarge

The solution here is to redirect the request back to the same domain used originally after authentication. To control where Azure AD sent the authenticated request back to the application, set the OpenIdConnectAuthentications.RedirectUri property in the ConfigureAuth method below.

Note: the reply URL will also need to be configured in Azure Active Directory’s App Registration, otherwise you may run into this error.

[gist id=”f52d201787e249cc3eb82f34f53c4e00″ file=”Startup.Auth.cs”]

The application is opened from an Office Document

The scenario usually involves the user clicking on a link that opens an Azure AD protected OIDC application in Excel, Word, or any other Office document and they see the error after providing credential. The problem here is that there are multiple processes (Office and browser) involved and they don’t share cookies with each other. In the screen shot below the Word process originally navigates to the web site (frame 24 to frame 35). It then transitions the navigation over to the browser process (starting in frame 36). The nonce cookie is set originally in frame 32 in the word process when the request is redirected to Azure AD for authentication. In frame 60, the request is redirected back to the application in the browser process with an authenticated token and this is where the error occurs since the browser process does not have any nonce cookie.

hover over image to enlarge

This is a known issue due to the way Office handles http binding and how cookies work and has been discussed extensively in the following links.  There are also a few proposed workarounds in these links:

https://github.com/aspnet/AspNetKatana/issues/78

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

https://support.microsoft.com/en-us/help/899927/you-are-redirected-to-a-logon-page-or-an-error-page-or-you-are-prompte

https://stackoverflow.com/questions/2653626/why-are-cookies-unrecognized-when-a-link-is-clicked-from-an-external-source-i-e

https://support.microsoft.com/en-us/help/838028/how-documents-are-opened-from-a-web-site-in-office-2003

Below are some ideas to work around this issue:

  1. Set the following registry key (on 64 bit OS) on the client machine where browsing is performed to disable hlink binding (see https://social.technet.microsoft.com/Forums/en-US/fe8f827f-a801-4fc0-b820-8dc30f816467/unable-to-hyperlink-to-company-applications-office-2016-proplus for more detail)

    [HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Office\9.0\Common\Internet]
    “ForceShellExecute”=dword:00000001

  2. Disable nonce cookie checking (not recommended since this has security impact)

    app.UseOpenIdConnectAuthentication(

        new OpenIdConnectAuthenticationOptions

        {

            ClientId = clientId,

            Authority = authority,

            PostLogoutRedirectUri = postLogoutRedirectUri,

            ProtocolValidator = new Microsoft.IdentityModel.Protocols.OpenIdConnectProtocolValidator()

            {

                RequireNonce = false

            }

        });

  3. Create a landing page without authentication and then use the landing page to redirect to the real site.  Use the landing page to open from Office documents.
  4. Perform browser detection in the web application to check for request from Office and return an empty html page as referenced in https://gist.github.com/bachoang/942a8223a689ceba77987b9c3f54ab61

    [gist id=”942a8223a689ceba77987b9c3f54ab61″ file=”OfficeDetection.cs”]

The OpenID Connect Nonce cookie does not have these attributes set:  SameSite=None and secure

Due to the recent SameSite cookie security update changes as documented in How to handle SameSite cookie changes in Chrome browser – Microsoft identity platform | Microsoft Docs and in Work with SameSite cookies and the Open Web Interface for .NET (OWIN) | Microsoft Docs, cookies involved in the authentication process, including the Nonce cookies, should have both of these attributes set:  SameSite=None and secure.  Cookies not having both of these attributes may get dropped by the browser, which will result in the above error.  Here is what a Fiddler trace of a working scenario should look like for the request before redirecting to Azure AD for login showing how the Nonce cookie is set:

 

To make sure both of the above requirements are satisfied you should

  1. use https protocol to navigate to the web application and
  2. For .Net application, update the .Net framework to version 4.7.2 or greater and these nuget packages – Microsoft.Owin.Security.OpenIdConnect and Microsoft.Owin to version 4.1.0 or greater.  For .Net Core applications, update the .Net Core framework to version 2.1 or greater for .Net Core v2 apps and .Net Core 3.1 for .Net Core v3 apps.

With the above configuration, here is my working code for Startup.Auth.cs

using System.Configuration;
using Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.OpenIdConnect;
using System.Threading.Tasks;
using Microsoft.Owin.Security.Notifications;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;

namespace NetWebAppOIDC2
{
    public partial class Startup
    {
        private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
        private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
        private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
        private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
        private static string authority = aadInstance + tenantId;

        public void ConfigureAuth(IAppBuilder app)
        {
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

            app.UseCookieAuthentication(new CookieAuthenticationOptions());
            app.UseOpenIdConnectAuthentication(
                new OpenIdConnectAuthenticationOptions
                {
                    ClientId = clientId,
                    Authority = authority,
                    PostLogoutRedirectUri = postLogoutRedirectUri,
                    RedirectUri = "https://localhost:44313",
                    
                    Notifications = new OpenIdConnectAuthenticationNotifications
                    {
                        AuthenticationFailed = OnAuthenticationFailed
                    }

                    // Don't use SystemwebCookieManager class here to override the default CookieManager as that seems to negate the SameSite cookie attribute being set
                    // CookieManager = new SystemWebCookieManager()

                });
        }

        private Task OnAuthenticationFailed(AuthenticationFailedNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> context)
        {
            context.HandleResponse();
            context.Response.Redirect("/?errormessage=" + context.Exception.Message);
            return Task.FromResult(0);
        }
    }
}

Setup POSTMAN to get Azure access tokens automatically for you

Introduction

Postman is an HTTP request tool that is very handy for developing and testing your Azure requests. This is the Postman website: https://learning.getpostman.com/

Postman does make it easy to setup authentication and acquire access tokens but it normally is a multi-step process. The purpose of this blog post is to show you how you can setup Postman to automatically handle authentication for you so you don’t have to go get a new token manually to test with. This example will concentrate on using the Client_Credentials flow targeting Microsoft Identity Platform V2 endpoint.

Client credentials flow V1 endpoint

Client credentials flow V2 endpoint

Setup the Environment

The first step in this process is to setup your environment so you can create variables that will be used in your headers and or body. To do this, you can use the “New” button on the upper left corner of the screen. There is also a gear icon in the upper right hand corner of the screen where you can create the environment as well but lets start with the New button

Click on “New” à “Environment” and give it a meaningful name. In my example, I am using a dummy tenant name.

Lets add some environment variables in the Variable chart for that environment. You could add the variables on the collection side but I prefer using the environment so I can switch values easily just by selecting the environment.

Add the following variables:

  • client_id
  • client_secret
  • token_endpoint
  • scope
  • access_token

Set the initial and current values on the variables. Make sure you have a properly setup app registration with Microsoft Graph application permissions for User.Read.All to test this script and you have performed admin consent on those permissions. Also, note that all variables are case sensitive!

The V2 endpoint would look like this: https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token/ (where {tenant} is where you would also add the tenant id – you cannot use a variable here )

Note: If you’re going to hit the v1 endpoint, you will not be using a scope parameter and would instead, need the resource parameter.

The scope would obviously depend on the resource you’re needing a token for. For Microsoft Graph, it would be: https://graph.microsoft.com/.default — just pulling the Microsoft graph permissions off your app registration. Your access token would then be for Microsoft Graph.

Next, create a collection to save your scripts to

Click on “New” à Collection. Give your collection a meaningful name and description if you like, then on the Pre-requests Scripts tab, add this script:

[gist id=”9bb52b6f4c4df275a7b6350e1a10fbd4″]

Then click update.

Build a simple Test Request

Now, build a simple request and save it into the Collection folder you have created. You can build a new request by right clicking on the new collection you’ve just created and then selecting “Add Request” and it will automatically be added to the collection.

See how I’ve used my variable names in key places for this request.

My URL for the GET request is: https://graph.microsoft.com/beta/users

I’ve added one simple Header for “Authorization” and set the value to: “Bearer {{access_token}}”

If you did not right click on the collection to add a new request, you can save the request setup to the collection by clicking on the Save drop-down box, then Save As:

Be sure to select the collection you created that has the pre request script on it and then save:

Run the request

When running your request now, make sure you have the correct environment selected to get the proper variable values

Click the send button and you should get the list of users in your tenant.

View the console for additional information

As you can see in the script, I am outputting some information for you into the console window. You can show the console window before running the script by clicking on View à Show Postman Console

You can see information in the console window such as the token that was generated and the calls that were made:

Summary

You can setup postman to make building requests for testing and troubleshooting purposes for the client_credentials flow by easily setting up a few variables, adding the pre-request script and then plugging the variables into your request. Each time the request is sent, you can get a new access token and use that as the bearer token for the request. You can open the console screen in postman (View/console) and see the token that was generated if you want to view that in http://jwt.ms as the script is outputting the token into the console, for additional troubleshooting purposes.

References

https://liftcodeplay.com/2018/03/18/how-to-automatically-set-a-bearer-token-for-your-postman-requests/

Understanding Azure AD token signing certificate (kid)

Introduction

Upon successful authentication, Azure AD issues a signed JWT token (id token or access token). The resource application needs to know the public key of the certificate used sign the token in order to validate the token signature. Depending upon the type (OAuth2 or SAML Application) of the resource application, the steps to obtain the pubic key information are different. An OWIN asp.net application can throw the following error when it’s not able to find the kid to validate the token signature:

IDX10501: Signature validation failed. Unable to match ‘kid’ or IDX10501: Signature validation failed. Unable to match key

The setup:

To demonstrate the concept, I have registered the following 3 Applications in Azure AD:

bhfrontend app – used to sign in and get an access token to one of the following backend app:
bhbackend app – backend oauth2 application which the bhfrontend app requests an access token for
SharepointSAMLTest app – backend SAML application which the bhfrontend app requests an access token for

  1. bhbackend – OAuth2 app (created from Azure Active Directory -> App Registration -> New Registration in the Azure portal).

  1. SharepointSAMLTest – SAML application (created from Azure AD -> Enterprise Application -> New Application -> Non-gallery application -> configure SAML Single sign-on). Note that Azure AD automatically assigns certificate to this application. This certificate is used to sign both a SAML token and an oauth2 token.

  1. bhfrontend – OAuth2 web app (created the same way as bhbackend above). In the API permissions add both bhbackend and SharepointSAMLTest app and grant admin consent. You will also need to create a password secret for this application (to be used later in an authorization code flow to get an access token). This application is used for sign in and get an access token for both the bhbackend app and the SharepointSAMLTest app.

OAuth2 resource application

Use Postman’s Authorization Code flow to obtain an access token for the bhbackend app. For oauth2 applications, use the following keys discovery endpoint to find the public key of the signing certificate: https://login.microsoftonline.com/common/discovery/keys 

I am using https://jwt.ms to decode the access token and kid in the access token should match one of the 3 kids at the end point.

Note: The application demonstrated here is a V1 app. For V2 app, use https://login.microsoftonline.com/common/discovery/v2.0/keys instead of.

SAML Resource Application

Again, use Postman to get an access token for the SharepointSAMLTest App. Azure AD uses the certificate created for this application to sign the token. For this scenario, we have to use the following keys discovery endpoint to get to the public key of that certificate. The App ID below can be found in the Properties section of that Enterprise App.

https://login.microsoftonline.com/<tenant name>/discovery/keys?appid=<SAML App ID>

The above keys discovery endpoint can be obtained from the app-specific metadata endpoint:

https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration?appid=<SAML App ID>

This is also documented below for both V1 and V2 endpoint:

V2 OpenID Connect protocol
V1 OpenID Connect protocol

If your app has custom signing keys as a result of using the claims-mapping feature, you must append an appid query parameter containing the app ID in order to get a jwks_uri pointing to your app’s signing key information. For example: https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration?appid=6731de76-14a6-49ae-97bc-6eba6914391e contains a jwks_uri of https://login.microsoftonline.com/{tenant}/discovery/v2.0/keys?appid=6731de76-14a6-49ae-97bc-6eba6914391e.

Typically, you would use this metadata document to configure an OpenID Connect library or SDK; the library would use the metadata to do its work. However, if you’re not using a pre-built OpenID Connect library, you can follow the steps in the remainder of this article to do sign-in in a web app by using the Microsoft identity platform endpoint.

In the example above I use the bhfrontend application as OAuth2 application for sign in.  If this application is a SAML Application with Single Sign-On feature enabled, the same Signature Validation error can occur here.  To resolve the error one should use the application-specific metadata endpoint.  See below examples for both the OpenID Connect OWIN middleware of JWT Bearer Authentication middleware usage of the MetadataAddress property.

OpenID Connect middleware:
use OpenIdConnectAuthenticationOptions.MetadataAddress Property

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        // Sets the ClientId, authority, RedirectUri as obtained from        web.config
        ClientId = clientId,
        Authority = authority,
        RedirectUri = redirectUri,
        MetadataAddress = "https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration?appid=<SAML App ID>"
        PostLogoutRedirectUri = redirectUri,
    }

JWT Bearer Authentication middleware:
use JwtBearerOptions.MetadataAddress Property

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    Audience = "...",
    Authority = "...",
    MetadataAddress = "https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration?appid=<SAML App ID>",
    TokenValidationParameters = new TokenValidationParameters
    {
       ...
    }
});

Microsoft.Identity.Web Web App Authentication:

services.AddMicrosoftIdentityWebAppAuthentication(Configuration)
     .EnableTokenAcquisitionToCallDownstreamApi()
     .AddInMemoryTokenCaches();
services.Configure<MicrosoftIdentityOptions>(options => { options.MetadataAddress = "https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration?appid=<SAML App ID>"; });

Microsoft.Identity.Web Web API Authentication:

using Microsoft.AspNetCore.Authentication.JwtBearer;
services.AddMicrosoftIdentityWebApiAuthentication(Configuration);
services.Configure<JwtBearerOptions>(JwtBearerDefaults.AuthenticationScheme,options => { options.MetadataAddress = "https://login.microsoftonline.com/<tenant name>/.well-known/openid-configuration?appid=<SAML App ID>"; });

Conclusion

I hope this blog post provides clarification on how to get to the public key of the signing certificate to validate the token signature. Please leave us a comment if you find this helpful. If you want to learn more about Azure AD signing keys rollover, you should take a look at this article.

Azure Active Directory reporting API with MS Graph

Introduction

As documented here, the Microsoft Graph Reporting API can be used to get the audit logs (https://graph.microsoft.com/beta/auditLogs/directoryAudits) and the sign-ins reports (https://graph.microsoft.com/beta/auditLogs/signIns). The old reports endpoint using Azure AD Graph is now deprecated

and attempting to use that legacy end point to get the auditEvents can result in the following error:

GET https://graph.windows.net/<tenant>.onmicrosoft.com/reports/auditEvents?api-version=beta

{

  “error”:{

    “code”:”Deprecated_ActivityReports”,”message”:”This API is deprecated as part of old azure classic portal, please use the API https://graph.windows.net/$tenantdomain/activities/. For more information refer the documentation https://docs.microsoft.com/en-us/azure/active-directory/active-directory-reporting-api-getting-started-azure-portal.. CorrelationID:10242ee6-2aa7-4173-826a-dc0fe898a87b timestamp:2019-04-12 13:00:05Z”

  }

}

Using the new end point…

Make sure you refer to the Reporting API prerequisite page for pre-requisites on accessing the Azure AD reporting API. For Application Permission token, make sure the application has AuditLog.Read.All Application permission and that  permission is given admin consent.

Note that the reporting API guidance at https://github.com/toddkitta/azure-content/blob/master/articles/active-directory/active-directory-reporting-api-getting-started.md is outdated. In this blog post, I’ll show how to modify the PowerShell script to use the new MS Reporting API to get the Audit logs for the last 2 days. The new endpoint we want to use is https://graph.microsoft.com/beta/auditLogs/directoryAudits. See documentation for more info on directoryAudits. Below is the entire PS script using reporting API.

NoteYou still need to follow the set up instruction at https://github.com/toddkitta/azure-content/blob/master/articles/active-directory/active-directory-reporting-api-getting-started.md to create an application registration and make sure to configure Microsoft Graph for API permission.  Replace the ClientID, ClientSecret, and tenantdomain with your information.

[gist id=”0ce05bbc3ff4fcb51b3a61e0353c404e” file=GetReportingApi.ps1″”]

The above script can be easily modified to get the sign-ins report.  An Azure AD Premium P1 license is required to get the sign-ins data.  Please review Azure AD audit log API overview and Reporting API tutorial prerequisite for more detail.

Thanks for reading and drop us a comment if this content helps.

AADSTS50011: The reply url specified in the request does not match the reply urls configured for the application

Last Updated: August 23 2019

Let’s get started…

When your developing or integrating an application with Azure AD, you might see the following similar error…


AADSTS50011: The reply url specified in the request does not match the reply urls configured for the application: ‘XXX’.


This is because the redirect_uri (when using OpenID Connect) or AssertionConsumerServiceUrl (when using SAML2) being passed to Azure Active Directory to sign-in, does not exist in the application registration.


For example, if using OpenID Connect, your authentication request might look something like this…

https://login.microsoftonline.com/common/oauth2/authorize?response_type=code
&client_id=99f00653-5600-45d1-aa19-57a297ad0a58&redirect_uri=https://www.contoso.com/signin-oidc

If you are signing in using a browser, you can also see this sign-in request in the browsers address bar on the error screen.

So, we are going to check if https://www.contoso.com/signin-oidc is a reply address in the application registration for 99f00653-5600-45d1-aa19-57a297ad0a58

We got the error because https://www.contoso.com/signin-oidc is not added as a reply address in the application registration.


For SAML authentication, The sign-in request might look something like this…

https://login.microsoftonline.com/common/saml2?SAMLRequest=jZHNS8NAEMXvgv9D2Hu...

The SAMLRequest is going to look like a long value of random characters. We call this a Base64 encoded string that is SAML enflated. So to ‘deflate’ this in order to read the contents of the SAMLRequest, you can use the TextWizard in Fiddler or use a tool like the one below…
https://www.samltool.com/decode.php

Once the SAMLRequest is deflated, it will look something like this…

<samlp:AuthnRequest
xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
ID="id6c1c178c166d486687be4aaf5e482730"
Version="2.0" IssueInstant="2013-03-18T03:28:54.1839884Z"
xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
AssertionConsumerServiceURL="https://www.contoso.com">
<Issuer xmlns="urn:oasis:names:tc:SAML:2.0:assertion">https://www.contoso.com</Issuer>
<NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"/>
</samlp:AuthnRequest>

The Assertion Consumer Service (ACS) within the SAMLRequest resembles the reply address.

So, we are going to check if ‘https://www.contoso.com‘ is a reply address in the application registration for the application ‘https://www.contoso.com’ which is identified by the apps ‘Identifier (Entity ID)’ in Azure Active Directory.

So what’s the solution?

So, to resolve this, you guessed it, ensure the redirect URI or Assertion Consumer Service URL is added to the application registration.

To do this…

  1. Sign into the Azure portal @ https://portal.azure.com
  2. Go to Azure Active Directory.
  3. Go to Application registrations.
  4. Find your app.
  5. Go to Authentication under Manage.
  6. Review your registered Redirect Uri(s).

How to determine what reply address is being used…

If you’re not sure how to collect this information, we generally like to use a HTTPS capturing tool like Fiddler (Available for Windows, macOS, and Linux).

To learn more about how you can use Fiddler, see the following article…

https://blogs.aaddevsup.xyz/2018/09/12/capture-https-traffic-with-http-fiddler/

You can look at the details of your request to Azure AD. Use the following article as a guide…
https://docs.telerik.com/fiddler/Observe-Traffic/Tasks/ViewSessionContent

Certain User Attributes may not show up in Graph Query

The Problem

Azure AD Connect Sync Tool is often used to sync on prem Active Directory users and their attributes to Azure Active Directory. To query for these user and other directory objects, the Graph REST endpoint (Azure AD Graph or Microsoft Graph) can be used. An example Microsoft Graph query to get a User is the following:

https://graph.microsoft.com/beta/users/john@contoso.com

In certain scenario, some attributes, whether they are synced as Directory Extension or as regular attribute, may not show up in the Graph query because they are multi-valued (MV). This is loosely documented here

An example multi-valued attribute is the departmentNumber attribute. To see if an attribute is an MV attribute, you can use the Active Directory Schema MMC Snap-In

 

So how can I query for this…

If you have Exchange installed in your environment, most likely your Active Directory Schema has been extended to include 15 User ExtensionAttributes (ExtensionAttribute1 – 15). You can just copy this value into one of the ExtensionAttributes and re-sync them to Azure AD. These will show up under the onPremisesExtensionAttributes collection for the User object.

Using “groups” claim in Azure Active Directory

Lets get Started!

To enable the return of groups in a claim, there are two ways…

  1. Use the application registration manifest by enabling the groupMembershipClaims property…
    https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-app-manifest
  2. or if it’s a SAML application, you can enable it though the SSO configuration.

The steps on enabling groups claim is outlined in the following article…
https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-group-claims

Once enabled, groups will now be returned in the “groups” claim within a access token or ID token using OpenID Connect.


Important Note:

Id tokens will only contain the groups claim if the openid value is included in the scope parameter.


When using, the Azure Active Directory Authentication library (ADAL) for dotnet, by default you may not get the groups claim. You may need to add the scope claim with the openid value as an ExtraQueryParameter.


Moving forward…

The following groups claim description comes from https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens

Provides object IDs that represent the subject’s group memberships. These values are unique (see Object ID) and can be safely used for managing access, such as enforcing authorization to access a resource. The groups included in the groups claim are configured on a per-application basis, through the groupMembershipClaims property of the application manifest. A value of null will exclude all groups, a value of “SecurityGroup” will include only Active Directory Security Group memberships, and a value of “All” will include both Security Groups and Office 365 Distribution Lists.


Examples…

For example, if you are using OAuth2 and getting a access token for Microsoft Graph, the groups claim will not be returned. This is because the application registration of Microsoft Graph does not have the groupMembershipClaims enabled.

Keep in mind, only the audience of the access token should be consuming the access token. In this case, the audience is Microsoft Graph. Microsoft Graph currently has no need to know what groups a user is a member of.

In another example, if the audience is Azure Storage Explorer, this first-party application registration does in fact return groups because it does have groupMembershipClaims enabled. This is because Azure Storage Explorer allows you to assign groups to access its resources so it needs to know what groups a principal is a member of.

So, if you want to get the groups claim from an access token, ensure the audience is for your application registration. Then you can have your application request a access token for another resource like Microsoft Graph. Otherwise, you can use the ID token issued by Azure AD when using OpenID Connect.

An Access Token and ID Token will look something like this with the “groups” claim…

{
“typ”:
“JWT”,
“alg”: “RS256”,
“x5t”: “{x5t-value}”,
“kid”: “{kid-value}”
}
.{
“aud”: “{audience}”,
“iss”: “https://sts.windows.net/{tenant-id}/”,

“groups”: [ “group-id-1”, “group-id-2”,
…}.{signature}


There are some limitations…

GUID’s returned only.

By default, GUID’s are returned in the “groups” claim. If your group is synchronized to Azure AD using Azure AD Connect, you can then display the group name.

For more information about configuring the groups claim…
https://docs.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-fed-group-claims

Otherwise, if they are not synchronized, you will only get the GUID. You will need to make a separate call to Microsoft Graph to retrieve another value like its Display Name.

The Microsoft Graph call will look something like this…

https://graph.microsoft.com/beta/users/{user-id}/memberOf

If using Azure AD Graph API (not recommended)….
https://graph.windows.net/{tenant-ID}/users/{user-ID}/getMemberObjects


When using the OAuth2 implicit flow and the principal is a member of more than 5 groups…

Instead of getting a “groups” claim, you will get a “hasgroups” claim.

The following description is from https://docs.microsoft.com/en-us/azure/active-directory/develop/access-tokens

“hasgroups” claim: If present, always true, denoting the user is in at least one group. Used in place of the groupsclaim for JWTs in implicit grant flows if the full groups claim would extend the URI fragment beyond the URL length limits (currently 6 or more groups). Indicates that the client should use the Graph to determine the user’s groups (https://graph.windows.net/{tenantID}/users/{userID}/getMemberObjects).

In this scenario, An Access Token and ID Token will look something like this with the “groups” claim…

{
“typ”:
“JWT”,
“alg”: “RS256”,
“x5t”: “{x5t-value}”,
“kid”: “{kid-value}”
}
.{
“aud”: “{audience}”,
“iss”: “https://sts.windows.net/{tenant-id}/”,

“hasgroups”: “true”,
…}.{signature}


For all other authentication flows…

For other flows, if the number of groups the user is in goes over a limit (150 for SAML, 200 for JWT), then an overage claim will be added to the claim sources pointing at the Graph endpoint containing the list of groups for the user. So, just like above, you will need to make a additional call to the Graph API endpoint to get the groups the user is a member of.

In this scenario, An Access Token and ID Token will look something like this with the “groups” claim…

{
“typ”:
“JWT”,
“alg”: “RS256”,
“x5t”: “{x5t-value}”,
“kid”: “{kid-value}”
}
.{
“aud”: “{audience}”,
“iss”: “https://sts.windows.net/{tenant-id}/”,

“_claim_names”: { “groups”: “src1” },

“_claim_sources”: {

“src1”: { “endpoint”: “https://graph.windows.net/{tenantID}/users/{userID}/getMemberObjects” }

},
…}.{signature}


Azure AD B2C

As for Azure AD B2C, when using user flow policies, the “groups” claim is not available. You will have to use the Graph API to get the list of groups the user is a member of.


Using Microsoft Graph to get a list of groups a user is a member of.

Even though today, the endpoints provided by the claims above when the user is a member of too many groups is using the Azure AD Graph endpoint, we actually recommend using Microsoft Graph as the Azure AD Graph is being deprecated.

You can use Microsoft Graph to get a list of groups the user is a member of by following the guidance provided in the following article…

https://docs.microsoft.com/en-us/graph/api/user-list-memberof?view=graph-rest-1.0

Essentially, the Microsoft Graph call will look something like this…

https://graph.microsoft.com/v1.0/users/john@contoso.com/memberOf

Retrieving Azure Key Vault Secret using System MSI in an Azure VM in Python

Introduction

This is a walk-through showing how to use System Managed Service Identity (MSI) from an Azure VM to retrieve an Azure Key Vault secret in python.

Pre-requisite

To use the steps in this walk-through you need to have the following:

  1. Azure VM
  2. Azure Key Vault
  3. Python is already installed in the Azure VM (can be downloaded at https://www.python.org/downloads/)

The steps

  1. Enable System MSI for the Azure VM in the Azure portal (on the selected Azure VM blade -> Go to the Identity section -> change the Status to ‘On’ in the System assigned tab -> Save)

  2. Install Azure Key Vault for python in the Azure VM

    RDP to the Azure Key Vault machine and open a command line and run “pip install azure-keyvault

  3. Configure the System MSI to have access to your Azure Key Vault in the Azure portal (from the selected Azure Key vault -> Access policies -> Add new)

    In the ‘Select principal’ section select the Azure VM MSI that you just created in step 1 above. The MSI has the same name as the Azure VM. Also remember to give appropriate permissions (Key permissions, secret permissions, and certificate permissions) to the MSI depending on what you want to do with the MSI. In this walk-through I’ll use the MSI to retrieve the secret so it’s sufficient to give just give ‘Get’ access in the secret permissions section.

  1. Create a ‘test.py’ python script file with the following content:
from azure.keyvault import KeyVaultClient
from msrestazure.azure_active_directory import MSIAuthentication

credentials = MSIAuthentication(resource='https://vault.azure.net')
kvclient = KeyVaultClient(credentials)
res = kvclient.get_secret("https://<vault name>.vault.azure.net/", "<secret name>", "").value
print(res)
  1. Run the above script and you should get your secret value printed out

Conclusion

The above step demonstrates a simple way to use System MSI to retrieve an Azure Key Vault secret. The script use the MSIAuthentication class for MSI authentication to Azure AD and get an access token for Azure key vault.

References

How to use managed identities for Azure resources on an Azure VM with Azure SDKs

Azure Key Vault Developer’s Guide