I have an Angular SPA that calls a C# web api. I would like to be able to use the token I receive during login to authorize access to my API. I would ideally like to accomplish this through the use of the [Authorize] attribute built into c# web api’s. Has anyone had any success accomplishing this? Can you point me towards documentation that I can look at?
First of all, I would recommend you use the authorization extension to manage permissions (scopes) for your API.
Let’s say you have an endpoint like /articles
through which calls can be made to create, read, update, and delete articles. You would first create, for example, the following permissions: create:article
, read:article
, update:article
, delete:article
that correspond to each action accordingly.
You can then create roles for different types of users, e.g., standard user, editor, administrator, and then assign the permissions to these roles as needed.
So for example, if standard users can only read articles, you would only assign read:article
to the standard user role, whereas for editor you would assign all of these permissions so they can create, update, read, and delete articles.
You will need to use a rule to include the assigned permissions to the scope
parameter in your user’s access token:
var permissions = user.permissions || [];
context.accessToken.scope = permissions.join(' ');
callback(null, user, context);
}`
In your application, you will need read the scopes from the token to determine what resources your user has access to.
Your .NET code to have the Authorize
annotation validate a scope would look something like this:
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
namespace WebAPIApplication
{
public class HasScopeRequirement : AuthorizationHandler<HasScopeRequirement>, IAuthorizationRequirement
{
private readonly string issuer;
private readonly string scope;
public HasScopeRequirement(string scope, string issuer)
{
this.scope = scope;
this.issuer = issuer;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
{
// If user does not have the scope claim, get out of here
if (!context.User.HasClaim(c => c.Type == "scope" && c.Issuer == issuer))
return Task.CompletedTask;
// Split the scopes string into an array
var scopes = context.User.FindFirst(c => c.Type == "scope" && c.Issuer == issuer).Value.Split(' ');
// Succeed if the scope array contains the required scope
if (scopes.Any(s => s == scope))
context.Succeed(requirement);
return Task.CompletedTask;
}
}
}
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
string domain = $"https://{Configuration["Auth0:Domain"]}/";
services.AddAuthorization(options =>
{
options.AddPolicy("create:article",
policy => policy.Requirements.Add(new HasScopeRequirement("create:article", domain)));
options.AddPolicy("update:article",
policy => policy.Requirements.Add(new HasScopeRequirement("update:article", domain)));
options.AddPolicy("read:article",
policy => policy.Requirements.Add(new HasScopeRequirement("read:article", domain)));
options.AddPolicy("delete:article",
policy => policy.Requirements.Add(new HasScopeRequirement("delete:article", domain)));
});
Once you have set this all up, you can annotate your endpoints like so: [Authorize(Roles="create:article")]
.