Let's consider a resource to be a logical source that needs to be protected.
This means that the resource isn't bound to one WebApi, but the WebApi is bound to one resource. You can create a group of WebApi's that together form the resource. Or you can simply add the complete source to one WebApi.
It then makes no sense to put multiple resources into one WebApi. If it doesn't belong to the resource then create seperate WebApi's.
However, if it does belong to the same resource and you want to divide the resource in logical parts, then use scopes instead.
You can add multiple scopes to one resource:
resource = Api0
scope = Api1.Read
scope = Api1.Write
scope = Api2.Read
scope = Api2.Write
Please note that I used 'Api0' as the resource name (options.ApiName). Where ApiX may be a logical division per client.
Now I can create seperate WebApi's that are part of the same resource (they all have options.ApiName = "Api0"
), or one WebApi.
In case of seperate Api's, where each Api implements one scope, I can use something like this:
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "Api0";
options.JwtBearerEvents = new JwtBearerEvents
{
OnTokenValidated = context =>
{
if (!context.Principal.HasClaim("scope", "Api1.Read"))
context.Fail("Invalid Scope");
return Task.CompletedTask;
}
};
});
While in case of one WebApi with multiple scopes I can use Policies:
services.AddMvcCore()
...
.AddAuthorization(p =>
{
p.AddPolicy("Api1.Read", (policy) => policy.RequireScope("Api1.Read"));
p.AddPolicy("Api1.Write", (policy) => policy.RequireScope("Api1.Write"));
p.AddPolicy("Api2.Read", (policy) => policy.RequireScope("Api2.Read"));
p.AddPolicy("Api2.Write", (policy) => policy.RequireScope("Api2.Write"));
});
Where you can use the AuthorizeAttribute:
[Authorize("Api1.Read")]
Please note that scope != resource. The client requests one or more scopes, e.g. "Api1.Read Api1.Write"
, but the resource is validated by the name (audience=Api0).
The events, policies, middleware can be used for finer grained authorization.