Search code examples
c#architectureauthorizationasp.net-identityidentityserver4

Role and Policy Authorization without AspNetCore Identity?


We are currently moving our internal desktop applications over to a series of Web Apps with a unified Identity Server. As such, we are leveraging Identity Server to manage all of our user accounts and allowing it to federate access to our clients and APIs. Overall, this is all working great.

However, I'm not having much luck in finding any examples where someone has managed to implement role or policy-based authorization without utilizing AspNetCore Identity. With IdentityServer managing my users, I would prefer not to have to duplicate my users between my identity server and my individual webapps, and I'd instead prefer to have each webapp manage its own internal permissions.

Are there any examples on how to implement policy-based authorization without leveraging the entirety of AspNetCore Identity?


Solution

  • The answer to your question is to setup a seperate authentication server and seperate authorization server. As commented here by one of the creators of IdentityServer:

    IdentityServer is both an OAuth 2.0 and OpenID Connect implementation. Yes – we recommend to use IdentityServer for end-user authentication, federation and API access control.

    PolicyServer is our recommendation for user authorization.

    The article itself is worth reading, and here's a link to the PolicyServer website.

    The idea is quite simple, though there are different approaches possible. It's likely that the implementation will take some time because the free, open source version uses a local source, while you will probably want to use a centralized version. In which case you can consider to take a look at the commercial version first.

    In short, how it can work. First the user is authenticated. Then the authorization middleware adds the claims to the user. Then the user is authorized. In code, something like:

    app.UseAuthentication();
    
    // add this middleware to make roles and permissions available as claims
    // this is mainly useful for using the classic [Authorize(Roles="foo")] and IsInRole functionality
    // this is not needed if you use the client library directly or the new policy-based authorization framework in ASP.NET Core
    app.UsePolicyServerClaims();
    
    app.UseAuthorization();
    

    This will leave the authentication flow intact and 'opt-in' authorization for the user. To speed up performance you can use caching.

    The sub claim is the key. As part of the oidc flow, that's the one and only claim that remains constant.

    The OSS sample works fine for the Mvc client, but when you extend it with a resource (api) it will not work out of the box. Because the access token may not contain all the claims.

    And this is the essence, it's the resource that knows best what claims are relevant. So it should be the resource that requests the information.

    In fact this is what a local PolicyServer implementation does, a different policy json for the clients and api's. You can move this to a database and use the client/scope as discriminator, defining roles / permissions per user (sub).

    The api can use the access token to request information from the authorization server. To solve the problem where the client wants to know which functionality is allowed, e.g. for building menu's, you can extend the authorization server with an endpoint where it can request a list of roles / permissions.