1

I have an asp.net mvc app which authorizes with Azure AAD. The app is based on this github example:

https://github.com/dushyantgill/VipSwapper/tree/master/TrainingPoint

this app has a custom authorize attribute

  public class AuthorizeUserAttribute : AuthorizeAttribute
  {
        protected override void HandleUnauthorizedRequest(AuthorizationContext ctx)
        {
            if (!ctx.HttpContext.User.Identity.IsAuthenticated)
                base.HandleUnauthorizedRequest(ctx);
            else
            {
                ctx.Result = new ViewResult { ViewName = "Error", ViewBag = { message = "Unauthorized." } };
                ctx.HttpContext.Response.StatusCode = 403;
            }
        }
    }

However this seems very odd to me.

I have on a controller something like this:

 public class GlobalAdminController : Controller
    {
        // GET: GlobalAdmin
        [AuthorizeUser(Roles = "admin")]
        public ActionResult Index()
        {
            return View();
        }
    }

As you can see the custom attribute is used there, but take a deeper look at the code of the custom attribute. Apparently on both the if and ELSE the request is not authenticated.

Now take a look at this screenshot.

Makes no sense right? http://screencast.com/t/obqXHZJj0iNG

The question, is what should I do to allow the user to execute the controller?

Update 1: On my auth flow I have the following

  public void ConfigureAuth(IAppBuilder app)
        {
            // configure the authentication type & settings
            app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
            app.UseCookieAuthentication(new CookieAuthenticationOptions());

            // configure the OWIN OpenId Connect options
            app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
            {
                ClientId = SettingsHelper.ClientId,
                Authority = SettingsHelper.AzureADAuthority,
                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    // when an auth code is received...
                    AuthorizationCodeReceived = (context) => {
                        // get the OpenID Connect code passed from Azure AD on successful auth
                        string code = context.Code;

                        // create the app credentials & get reference to the user
                        ClientCredential creds = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);
                        string userObjectId = context.AuthenticationTicket.Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;

                        // use the ADAL to obtain access token & refresh token...
                        //  save those in a persistent store...
                        EfAdalTokenCache sampleCache = new EfAdalTokenCache(userObjectId);
                        AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, sampleCache);

                        // obtain access token for the AzureAD graph
                        Uri redirectUri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
                        AuthenticationResult authResult = authContext.AcquireTokenByAuthorizationCode(code, redirectUri, creds, SettingsHelper.AzureAdGraphResourceId);

                        if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
                            context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));

                        // successful auth
                        return Task.FromResult(0);
                    },
                    AuthenticationFailed = (context) => {
                        context.HandleResponse();
                        return Task.FromResult(0);
                    }
                },
                TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
                {
                    ValidateIssuer = false
                }
            });
        }

Check specially the IsAADAdmin method call

/// <summary>
        /// The global administrators and user account administrators of the directory are automatically assgined the admin role in the application.
        /// This method determines whether the user is a member of the global administrator or user account administrator directory role.
        /// RoleTemplateId of Global Administrator role = 62e90394-69f5-4237-9190-012177145e10
        /// RoleTemplateId of User Account Administrator role = fe930be7-5e62-47db-91af-98c3a49a38b1
        /// </summary>
        /// <param name="objectId">The objectId of user or group that currently has access.</param>
        /// <returns>String containing the display string for the user or group.</returns>
        public static bool IsUserAADAdmin(ClaimsIdentity Identity)
        {
            string tenantId = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
            string signedInUserID = Identity.FindFirst(System.IdentityModel.Claims.ClaimTypes.NameIdentifier).Value;
            string userObjectID = Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;

            ClientCredential credential = new ClientCredential(SettingsHelper.ClientId, SettingsHelper.ClientSecret);

            // initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's EF DB
            AuthenticationContext authContext = new AuthenticationContext(SettingsHelper.AzureADAuthority, new EfAdalTokenCache(signedInUserID));

            AuthenticationResult result = authContext.AcquireTokenSilent(
                SettingsHelper.AzureAdGraphResourceId, credential, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));

            HttpClient client = new HttpClient();

            string doQueryUrl = string.Format("{0}/{1}/users/{2}/memberOf?api-version={3}",
                SettingsHelper.AzureAdGraphResourceId, tenantId,
                userObjectID, SettingsHelper.GraphAPIVersion);

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, doQueryUrl);
            request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
            HttpResponseMessage response = client.SendAsync(request).Result;

            if (response.IsSuccessStatusCode)
            {
                var responseContent = response.Content;
                string responseString = responseContent.ReadAsStringAsync().Result;
                var memberOfObjects = (System.Web.Helpers.Json.Decode(responseString)).value;

                if (memberOfObjects != null)
                    foreach (var memberOfObject in memberOfObjects)
                        if (memberOfObject.objectType == "Role" && (
                            memberOfObject.roleTemplateId.Equals("62e90394-69f5-4237-9190-012177145e10", StringComparison.InvariantCultureIgnoreCase) ||
                            memberOfObject.roleTemplateId.Equals("fe930be7-5e62-47db-91af-98c3a49a38b1", StringComparison.InvariantCultureIgnoreCase)))
                            return true;
            }

            return false;
        }

I am 100% sure the user is in the admin role, because when I debug it returns true and the claim is created

Update 2: At debugging time I checked the User.Claims and the role admin is in there. so I am not sure how this authorization per role works with User.IsInRole

http://screencast.com/t/zUbwbpzn55qb

9
  • The code you've posted only gets called when the user is not authorized, otherwise it uses the default behavior of the AuthorizeAttribute. So I don't understand what your problem is. Commented May 29, 2015 at 17:22
  • Authenticated and Authorized is two different things. It is possible the user is logged in but not admin, hence he is authenticated but not authorized. You need to add the user into webpages_UsersInRoles table as admin for him to be authorized. Commented May 29, 2015 at 17:23
  • I am sure the user is authenticated and its in the role admin, please see Update1, the code comes from the sample code I provided but I pasted it also here as well to provide more explanation Commented May 29, 2015 at 17:31
  • @EstebanV - But is User.IsInRole("admin") returning true? If not, then you won't be authorized. Commented May 29, 2015 at 17:39
  • yes indeed its returning false, but I am using the code from the provided sample, so what am I missing here? Commented May 29, 2015 at 17:44

2 Answers 2

3

Esteban, you seem to have missed setting the role claim type in your ConfigureAuth implementation. Please see line 55 of the sample: https://github.com/dushyantgill/VipSwapper/blob/master/TrainingPoint/App_Start/Startup.Auth.cs#L55. Once you do that User.IsInRole() and Authorize attribute will work properly.

Regd the implementation of the custom Authorize attribute - ASP.net has a bug where it returns a 401 error (instead of 403) for authorization failed (that puts the authenticated user in an endless auth loop with the IdP). This custom authorize attribute fixes that issue.

Hope that helps.

See you.

Sign up to request clarification or add additional context in comments.

1 Comment

thanks a lot, that fixed the issue. I am sure we'll keep in touch for other questions.!
0

The claim type for a role is http://schemas.microsoft.com/ws/2008/06/identity/claims/role (which you can shortcut to using ClaimTypes.Role per here).

I believe your ConfigureAuth class should be changed from this:

if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
                            context.AuthenticationTicket.Identity.AddClaim(new Claim("roles", "admin"));

To this:

if (GraphUtil.IsUserAADAdmin(context.AuthenticationTicket.Identity))
                            context.AuthenticationTicket.Identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.