0
votes

We have an Azure Mobile App using social network authentication. Trying to add user roles as claims using a custom token handler.

This all works when running on localhost -- the tokens are added in the token handler and they are available when the AuthorizationAttribute OnAuthorization method is called. The Authorize Attribute with the Roles specified works as expected.

But when running is Azure -- the claims are added but when the OnAuthorization method is called the custom role claims are gone.

Here is the code:

Startup/Config Class

public class OwinStartup
{
    public void Configuration(IAppBuilder app)
    {
        var config = GlobalConfiguration.Configuration;

        new MobileAppConfiguration()
        .AddPushNotifications()
        .ApplyTo(config);

        MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().
GetMobileAppSettings();

        app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions()
        {
            SigningKey = ConfigurationManager.AppSettings["authSigningKey"],
            ValidAudiences = new[] { ConfigurationManager.AppSettings["authAudience"] },
            ValidIssuers = new[] { ConfigurationManager.AppSettings["authIssuer"] },
            TokenHandler = new AppServiceTokenHandlerWithCustomClaims(config)
        });

        //Authenticate stage handler in OWIN Pipeline
        app.Use((context, next) =>
        {
            return next.Invoke();
        });

        app.UseStageMarker(PipelineStage.Authenticate);

    }

Token Handler that Adds the Role Claims

public class AppServiceTokenHandlerWithCustomClaims : AppServiceTokenHandler
{
    public AppServiceTokenHandlerWithCustomClaims(HttpConfiguration config)
        : base(config)
    {

    }

    public override bool TryValidateLoginToken(
        string token,
        string signingKey,
        IEnumerable<string> validAudiences,
        IEnumerable<string> validIssuers,
        out ClaimsPrincipal claimsPrincipal)
    {
        var validated = base.TryValidateLoginToken(token, signingKey, validAudiences, validIssuers, out claimsPrincipal);

        if (validated)
        {
            string sid = claimsPrincipal.FindFirst(ClaimTypes.NameIdentifier).Value;

            var roleProvider = UnityConfig.Container.Resolve<IRoleProvider>("RoleProvider");

            var roles = roleProvider.GetUserRolesBySid(sid);

            foreach (var role in roles)
            {
                ((ClaimsIdentity)claimsPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, role));
            }
        }
        return validated;
    }
}

Role Claim An example of a role claim from the identity claims collection

{http://schemas.microsoft.com/ws/2008/06/identity/claims/role: admin}

Authorize Attribute on Web Api Controller

[Authorize(Roles = "admin")]

Every call to an endpoint that has an Authorize attribute with one or more roles specified fails (401)

Not sure what is going on with the claims either getting stripped off or not persisted in the Identity when running in Azure.

thanks Michael

2

2 Answers

1
votes

I've got a chapter on this in the book - https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/#using-third-party-tokens

Note the bit about custom authentication with additional claims. You need to call a custom API with the original token, check the token for validity, then produce a new token (the zumo token) with the claims you want. You can then use those claims for anything that is required.

0
votes

According to this blog post, some of your options may be wrong. The AppSettings are only set for local debugging, and won't work in Azure.

Try this:

public void Configuration(IAppBuilder app)
{
    var config = GlobalConfiguration.Configuration;

    new MobileAppConfiguration()
        .AddPushNotifications()
        .ApplyTo(config);

    MobileAppSettingsDictionary settings = config
        .GetMobileAppSettingsProvider()
        .GetMobileAppSettings();

    // Local Debugging
    if (string.IsNullOrEmpty(settings.HostName))
    {
        app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions()
        {
            SigningKey = ConfigurationManager.AppSettings["authSigningKey"],
            ValidAudiences = new[] { ConfigurationManager.AppSettings["authAudience"] },
            ValidIssuers = new[] { ConfigurationManager.AppSettings["authIssuer"] },
            TokenHandler = new AppServiceTokenHandlerWithCustomClaims(config)
        });
    }
    // Azure
    else
    {
        var signingKey = GetSigningKey();
        string hostName = GetHostName(settings);

        app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
        {
            SigningKey = signingKey,
            ValidAudiences = new[] { hostName },
            ValidIssuers = new[] { hostName },
            TokenHandler = new AppServiceTokenHandlerWithCustomClaims(config)
        });
    }


    //Authenticate stage handler in OWIN Pipeline
    app.Use((context, next) =>
    {
        return next.Invoke();
    });

    app.UseStageMarker(PipelineStage.Authenticate);

}

private static string GetSigningKey()
{
    // Check for the App Service Auth environment variable WEBSITE_AUTH_SIGNING_KEY,
    // which holds the signing key on the server. If it's not there, check for a SigningKey
    // app setting, which can be used for local debugging.

    string key = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");

    if (string.IsNullOrWhiteSpace(key))
    {
        key = ConfigurationManager.AppSettings["SigningKey"];
    }

    return key;
}

private static string GetHostName(MobileAppSettingsDictionary settings)
{
    return string.Format("https://{0}/", settings.HostName);
}