Web API Authorization - ob1dev/Auth0 GitHub Wiki
The last step is to use the Policy-Based Authorization to make sure Auth0 Access Token contains the correct scope. It will give you the power to decorate desired endpoints with a list of scopes, which are required to invoke them.
Visual Studio
A policy requirement is made of two elements, a requirement class that holds just data, and an authorization handler that validates the data against the user.
Define requirements
Create a new class HasScopeRequirement
that implements the interface IAuthorizationRequirement
.
public class HasScopeRequirement : IAuthorizationRequirement
{
public string Issuer { get; }
public string Scope { get; }
public HasScopeRequirement(string issuer, string scope,)
{
this.Issuer = issuer;
this.Scope = scope;
}
}
An authorization requirement is a collection of data parameters that a policy can use to evaluate the Auth0 user principal. In the policy HasScopeRequirement
, the requirements are Token issuer (the claim iss
) and Token scope (the claim scope
).
Create authorization handler
Create a new class HasScopeHandler
that inherites the base class AuthorizationHandler
.
public class HasScopeHandler : AuthorizationHandler<HasScopeRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, HasScopeRequirement requirement)
{
if (!context.User.HasClaim(claim => claim.Type.Equals("scope") && claim.Issuer == requirement.Issuer))
{
return Task.CompletedTask;
}
var scopes = context.User.FindFirst(claim => claim.Type.Equals("scope") && claim.Issuer == requirement.Issuer).Value.Split(' ');
if (scopes.Any(s => s == requirement.Scope))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
An authorization handler is responsible for the evaluation of a requirement's properties. The authorization handler evaluates the requirements against a provided AuthorizationHandlerContext to determine if access is allowed.
The handler HasScopeHandler
determines if the current Auth0 principal has the claim scope
, which has been issued by a known and trusted Issuer. Authorization can't occur when the claim is missing, in which case a completed task is returned. When a claim is present, and the user's scope meets the define requirement, authorization is deemed successful. When authorization is successful, the context.Succeed
is invoked with the satisfied requirement as its sole parameter.
Add Authorization middleware
In the file Startup.cs
, modify the method ConfigureServices
as shown below:
public void ConfigureServices(IServiceCollection services)
{
string domain = $"https://{this.Configuration["Auth0:Domain"]}/";
services.AddAuthentication(options =>
...
services.AddAuthorization(options =>
{
options.AddPolicy("read:repositories", policy => policy.Requirements.Add(new HasScopeRequirement("read:repositories", domain)));
options.AddPolicy("create:repositories", policy => policy.Requirements.Add(new HasScopeRequirement("create:repositories", domain)));
options.AddPolicy("update:repositories", policy => policy.Requirements.Add(new HasScopeRequirement("update:repositories", domain)));
options.AddPolicy("delete:repositories", policy => policy.Requirements.Add(new HasScopeRequirement("delete:repositories", domain)));
});
services.AddSingleton<IAuthorizationHandler, HasScopeHandler>();
...
}
The above code configures custom policies read:repositories
, create:repositories
, update:repositories
and delete:repositories
, which require coresponding scopes that are issued by the issuer olegburov.auth0.com
.
Additionaly, the code adds the handler HasScopeHandler
to the services collection.
Secure API endpoints
Modify the class RepositoriesController
under the folder Controllers
, by decorating the actions with attribute Authorize
. Just like you did before for Web App.
public class RepositoriesController : Controller
{
...
[HttpGet]
[Authorize("read:repositories")]
public async Task<IEnumerable<RepositoryModel>> Get()
{
...
}
[HttpGet("{id}", Name = "GetRepository")]
[Authorize("read:repositories")]
public IActionResult Get(Guid id)
{
...
}
[HttpPost]
[Authorize("create:repositories")]
public IActionResult Post([FromBody] RepositoryModel repository)
{
...
}
[HttpPut("{id}")]
[Authorize("update:repositories")]
public async Task<IActionResult> Put(Guid id, [FromBody] RepositoryModel repository)
{
...
}
[HttpDelete("{id}")]
[Authorize("delete:repositories")]
public async Task<IActionResult> Delete(Guid id)
{
...
}
}
Summary
You've now used scopes from the Bearer token to protect endpoints. In the following tutorial, you'll learn more about obtaining the Access Token from Auth0 in Web App and send authorized requests to Web API.