Berend Hulshof
Berend Hulshof

Reputation: 1039

Handle the Role paramater of the Authorize tag in the AuthenticationHandler

So I'm trying to create a custom authorisation system which supports my custom token in my ASP.NET core web API. I know how to create custom handlers and all but I don't know one thing, I even don't know if it's even possible, how can I read the Role attribute from:

[Authorize(Roles="role")]

this is my custom authorisation handler:

public class Auth : AuthenticationHandler<AuthOptions>
{
    //private static Models.flowerpowerContext context = new Models.flowerpowerContext
    private Token.AuthToken authToken;
    private readonly RequestDelegate _next;
    private Models.flowerpowerContext _context;

    public Auth(IOptionsMonitor<AuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
        DbContextOptions<Models.flowerpowerContext> _options = new DbContextOptions<Models.flowerpowerContext>();
        _context = new Models.flowerpowerContext(_options);
        string test = options.ToString();



        authToken = new Token.AuthToken(_context);
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        Models.Token _token = new Models.Token();
        Token.Hasher hasher = new Token.Hasher();

        var bearer = Request.Headers["Authorization"];
        string token = bearer.ToString();



        _token = JsonConvert.DeserializeObject<Models.Token>(hasher.DecodeHash(token));

        string id = "";

        return AuthenticateResult.NoResult();
    }
}

The above handler works fine and it get's the token from the Authorization header. And the AuthOptions only have an empty constructor.

I couldn't find anything that would seem, at least to me, to retrieve the value of the Roles parameter. Does anybody know a better approach/work-around or a direct solution to this, and also if this is even possible?

EDIT: Forgot to mention why I want to do this; I want to do this because the role of the user has been defined within my custom token. And I need to compare it to the required role.

Upvotes: 1

Views: 1935

Answers (2)

Berend Hulshof
Berend Hulshof

Reputation: 1039

So after I followed CodeFuller's responses I came up with a solution. I basically I had to create a Claim collection like this:

IList<Claim> claimCollection = new List<Claim>
{
     new Claim(ClaimTypes.Email, _token.TokenEmail),
     new Claim(ClaimTypes.Role, _token.TokenRole)
};

Then I had to appoint a ClaimsIdentity and a ClaimsPrinciple. After this I had to create an AuthenticationTicket like this:

AuthenticationTicket ticket = new AuthenticationTicket(principal, "Custom scheme");

And all I had to do now was to return this:

return AuthenticateResult.Success(ticket);

This allowed me to verify roles from my custom token. This basically allowed me to do this:

[Authorize(Roles = "Admin")]

To get some more explanation about this matter you should check out CodeFuller's response.

Upvotes: 3

CodeFuller
CodeFuller

Reputation: 31312

You mix up two concepts here, authentication and authorization.

Authentication is the process of verifying who the user is.

Authorization is the process of verifying that the user has the access to requested resource.

Your Auth class is responsible for authentication, not authorization. If request is successfully authenticated, HandleAuthenticateAsync method should return instance of AuthenticateResult with filled Principal property of ClaimsPrincipal type. ClaimsPrincipal implements IPrincipal interface that has method bool IsInRole(string role).

Thus after the authentication step, it's known for the principal associated with the request, whether hi is in specific role. And the list of roles authorized for specific controller or action is specified in Roles property of AuthorizeAttribute. In your example if request principal is in "role", request will be successfully authorized. If he is not, request will fail with 401 or 403 error. This code of checking principal role is already implemented in AuthorizeAttribute filter. You don't need to reinvent it until you implement your custom authorization filter (I remind you that your Auth class is an authentication filter).

So answering your question about handling the user role: you should just return proper principal object that returns correct value for IsInRole(string role) method. All the rest will be handled by default authorization filter.

Upvotes: 3

Related Questions