aBlaze
aBlaze

Reputation: 2716

How to check if currently logged-in user is Azure Active Directory B2C "Global Administrator"? ASP.NET MVC

I would like only Users whose Directory Role is "Global Administrator" in my Azure Active Directory B2C tenant to be able to have access to a controller. Therefore, before the controller class is instantiated/run, I would like to perform a check similar to the following:

[Authorize(Roles = "Global Administrator")]
public class UserProfileController : Controller
{
    .... controller class ....
}

I understand that in order to call the "Authorize" command above, I need to enable the Role Manager to sync with Azure Active Directory B2C, like so in Web.config:

<authentication mode="Windows"/>
<roleManager enabled="true" defaultProvider="AspNetWindowsTokenRoleProvider">
  <providers>
    <clear />
    <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
  </providers>
</roleManager>

However, whenever the UserProfileController is created, then I get this error message:

"Method is only supported if the user name parameter matches the user name in the current Windows Identity."

And, in fact, I have confirmed that the following two items are unequal because userIdentityName gives the username of the Azure Active Directory B2C user currently logged into the web application, but windowsIdentityNames gives the username of me currently logged into my laptop:

string userIdentityName = User.Identity.Name;
string windowsIdentityName = WindowsIdentity.GetCurrent().Name;

This tells me that using the "Authorize" command above the UserProfileController is the wrong way to check if the currently logged in user is a "Global Administrator".

Thus my questions are:

  1. How can I check that the currently logged in user is a "Global Administrator" and prevent them from using UserProfileController if they are not?
  2. Should I disable the RoleManager because it is impossible to use in this scenario?
  3. Is there a way to check if the currently logged in user is a "Global Administrator" within the .cshtml also? I would like to display a button only for global admins.

======================================================

Edited to add the code for Startup.Auth.cs class:

public partial class Startup
{
    private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
    private static string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
    private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
    private static string tenantId = ConfigurationManager.AppSettings["ida:TenantId"];
    private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
    // This is the resource ID of the AAD Graph API.  We'll need this to request a token to call the Graph API.
    private static string graphResourceId = "https://graph.microsoft.com";
    private static readonly string Authority = aadInstance + tenantId;
    public static GraphServiceClient graphClient = null;

    public static GraphServiceClient GetGraphServiceClient()
    {
        return graphClient;
    }

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

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {
                ClientId = clientId,
                Authority = Authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,

                Notifications = new OpenIdConnectAuthenticationNotifications()
                {
                    // If there is a code in the OpenID Connect response, redeem it for an access token and refresh token, and store those away.
                   AuthorizationCodeReceived = (context) => 
                   {
                       var code = context.Code;
                       ClientCredential credential = new ClientCredential(clientId, appKey);
                       string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;

                       TokenCache userTokenCache = new ADALTokenCache(signedInUserID);

                       AuthenticationContext authContext = new AuthenticationContext(Authority, userTokenCache);
                       AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                           code, new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), credential, graphResourceId);

                       string token = result.AccessToken;

                       try
                       {
                           graphClient = new GraphServiceClient(
                               new DelegateAuthenticationProvider(
                                   (requestMessage) =>
                                   {
                                       requestMessage.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);

                                       return Task.FromResult(0);
                                   }));
                       }
                       catch (Exception e)
                       {
                           System.Diagnostics.Debug.WriteLine("Failed to create graph client: " + e.Message);
                       }

                       return Task.FromResult(0);
                   }
                }
            });
    }
}

============ UPDATE ============

This answer is very complete and answered this question: I found the answer to this question to be very helpful: Use AuthorizationAttribute to check user's roles

Upvotes: 1

Views: 5165

Answers (2)

aBlaze
aBlaze

Reputation: 2716

The answer to this question solved my problem.

`[Authorize(Roles = "admin")]` Infinite loop ASP.NET MVC and Azure Active Directory B2C

Upvotes: 1

Camilo Terevinto
Camilo Terevinto

Reputation: 32068

The problem is that you are using APIs that were created for Active Directory and attempting to consume Azure Active Directory. Azure Active Directory does not work, at all, with the built-in Membership providers.

The way your scenario is supported is through OAuth 2.0 / OpenId Connect. Read up "Developing ASP.NET Apps with Azure Active Directory" for the basics of working with Azure AD in ASP.NET applications.
Since the most relevant part of the code is omitted, this is what your Startup.Auth file would look like:

app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        ClientId = clientId,
        Authority = Authority,
        PostLogoutRedirectUri = postLogoutRedirectUri
    });

The exact configuration you use will vary depending on whether you need access to other Azure APIs using the user's credentials, so be sure to check the manuals for your required cases.

Just remember this:

  • authentication-mode: Windows => Active Directory
  • OAuth 2.0 / OpenId Connect => Azure Active Directory

As soon as you got the configuration right, you will be able to use [Authorize(Roles = "Global Administrator")] without any problem.

Upvotes: 1

Related Questions