Custom authorization with Azure AD Authentication in OWIN Web API

2.1k views Asked by At

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

  1. I can verify the token &
  2. 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?

2

There are 2 answers

0
Anand B On

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.

0
dthorpe On

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