0
votes

Using above service with Xamarin form, I have enable authentication with OAuth (Microsoft and Google). API call are working fine and those functions with [Authorize], are being enforced with authentication.

However, when defining specific role, it returns 401 error. I'm trying to create my own attribute from AuthorizationFilterAttribute class.

I'm storing the user info and roles in my custom tables : UserProfile and UserRoles. Hence, I'm not sure where to define these. Since its not reading data from these tables. If I try to run following, it returns empty result.

    [Authorize]
    [HttpGet]
    public IHttpActionResult Roles()
    {
        ClaimsIdentity claimsId = ClaimsPrincipal.Current.Identity as ClaimsIdentity;
        var appRoles = new List<String>();
        foreach (Claim claim in ClaimsPrincipal.Current.FindAll(claimsId.RoleClaimType))
            appRoles.Add(claim.Value);

        return Ok(appRoles);
    }

The closest article I came across in based on Web API : Web Api 2 and Owin

Edit Based on answer by Adrian, I added following code, but yet getting same 401 error.

public class MyAuthorize : System.Web.Http.Filters.AuthorizationFilterAttribute
{
    private string[] accessRoleNames;

    public MyAuthorize(params string[] roleNames)
    {
        accessRoleNames = roleNames;
    }

    public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        try
        {
            await Task.Delay(100);
           OnAuthorization(actionContext);
        }
        catch (Exception)
        {
            throw;
        }
    }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        WMSEntities db = new WMSEntities();

        string userID = actionContext.ControllerContext.RequestContext.Principal.Identity.Name;
        var user = db.UserProfiles.FirstOrDefault(x => x.User_ID == userID);

        if (user == null)
            actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        else
        {
            bool ok = false;
            foreach (var item in user.AppRoles)
            {
                foreach (string ar in accessRoleNames)
                {
                    if (item.Name == ar)
                        ok = true;
                }
            }
            db.Dispose();

            if (!ok)
                actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
        }
        base.OnAuthorization(actionContext);
    }
}

And referencing to my function as follows :

    [MyAuthorize(UserRole.Admin, UserRole.Collector)]
    [HttpGet]
    [Route("GetDataAdmin")]
    public string GetDataAdmin()
    {
        return "success access GetDataAdmin";
    }

If I debug the code locally, the OnAuthorization events are not being hit , hence not sure how to troubleshoot this.

1

1 Answers

1
votes

Within Azure Mobile Apps, you are using EntityFramework. You can do any query you want to this. Here is an example AuthorizationFilterAttribute that checks claims returned by an AAD credential:

using System.Linq;
using System.Net;
using System.Security.Principal;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Filters;
using Microsoft.Azure.Mobile.Server.Authentication;

namespace Backend.Helpers
{
    public class AuthorizeClaimsAttribute : AuthorizationFilterAttribute
    {
        string Type { get; }
        string Value { get; }

        public AuthorizeClaimsAttribute(string type, string value)
        {
            Type = type;
            Value = value;
        }

        public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
        {
            var request = actionContext.Request;
            var user = actionContext.RequestContext.Principal;
            if (user != null)
            {
                var identity = await user.GetAppServiceIdentityAsync<AzureActiveDirectoryCredentials>(request);
                var countOfMatchingClaims = identity.UserClaims
                    .Where(c => c.Type.Equals(Type) && c.Value.Equals(Value))
                    .Count();
                if (countOfMatchingClaims > 0) return;

            }
            throw new HttpResponseException(HttpStatusCode.Unauthorized);
        }
    }
}

This particular version allows you to do things like:

[AuthorizeClaims("groups","some-object-id")]

You can use this as a model. Where I check the GetAppServiceIdentityAsync(), you can check your database instead and decide whether the principal has a role.