read

I blogged about using Azure AD (AAD) groups as roles in an ASP.net MVC application a while ago. While speaking at Namesdays in Espoo, Finland last week, I presented briefly how the same approach can be applied in a WebAPI project. If you want to use AAD groups for adding role-based authorization to your APIs built with ASP.net WebAPI the story is similar but not identical.

Because in my case both solutions are based on the OWIN middleware, we can use the same approach of hooking up to OWIN “user authenticated” step for claims transformation - or maybe I should say enrichment.

Instead of using CookieAuthenticationOptions and CookieAuthenticationProvider, we can hook up to OAuthBearerAuthenticationProvider.OnValidateIdentity callback:

public void ConfigureAuth(IAppBuilder app)
{
	app.UseWindowsAzureActiveDirectoryBearerAuthentication(
		new WindowsAzureActiveDirectoryBearerAuthenticationOptions
		{
			Audience = ConfigurationManager.AppSettings["ida:Audience"],
			Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
			Provider = new OAuthBearerAuthenticationProvider()
			{
				OnValidateIdentity = AuthenticationOnValidateIdentity
			}
	});
}

The membership verification and claims process is identical to the in in the previous post but here it is again

private async Task AuthenticationOnValidateIdentity(OAuthValidateIdentityContext context)
{
    try
    {
        const string graphApiResourceId = "https://graph.windows.net";
        const string oidClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
        const string financeGroupId = "<enter group's object id from Azure AD portal>";
        const string claimIssuer = "WebApiApp";

        string tenantName = ConfigurationManager.AppSettings["ida:Tenant"];
        string authority = string.Format("https://login.windows.net/{0}", tenantName);
        string clientId = ConfigurationManager.AppSettings["ida:ClientID"];
        string clientSecret = ConfigurationManager.AppSettings["ida:Password"];

        var authContext = new AuthenticationContext(authority);
        var credential = new ClientCredential(clientId, clientSecret);

        // Get token to access AAD graph API
        var token = authContext.AcquireToken(graphApiResourceId, credential);
        
        var graphConnection = new GraphConnection(token.AccessToken);

        // check group membership from AAD
        var currentUserObjectId = context.Ticket.Identity.Claims.First(c => c.Type == oidClaimType).Value;
        var isMemberOf = graphConnection.IsMemberOf(financeGroupId, currentUserObjectId);
        
        // add Role claim to support asp.net Authorize attribute roles' validation
        if(isMemberOf)
            context.Ticket.Identity.AddClaim(new Claim(ClaimTypes.Role, 
                RestrictedGroupName, ClaimValueTypes.String, claimIssuer));
    }
    catch (Exception e)
    {
        // TODO: error logging
    }
}

As previously, this code needs both Microsoft.Azure.ActiveDirectory.GraphClient and Microsoft.IdentityModel.Clients.ActiveDirectory nuget packages to work.

NOTE! The above code doesn’t cache group membership and it will check it on every request - even if the currently requested API method wouldn’t require user to be a member of (any) group. Also Azure AD access tokens for checking the membership is also cached in-memory as that’s the default in ADAL. Please see this post on more information about ADAL token caching.

Comments

Blog Logo

Henri Tuomola


Published

Image

Henri Tuomola

Software developer and hobbyist photographer

Back to Overview