Search code examples
asp.netjwtopenid-connectaspnet-contrib

Central Authorization & Authentication Endpoint Using AspNet.Security.OpenIdConnect.Server (OIDC)


I am using Visual Studio 2015 Enterprise Update 1 and ASP.NET 5 rc1-final to build an endpoint that both issues and consumes JWT tokens as described in detail here. In this approach we have a single project that 'does it all' - the project uses OIDC to issue tokens, JWT bearer authentication to validate them and then guards access to various controllers using the Authorize attribute - all in the same project.

Now we would like to refactor this solution by creating an OIDC authorization & authentication endpoint that only issues and validates tokens. Then we want 'n' additional endpoints that rely on that OIDC endpoint as a central authority for authenticating tokens. This will allow us to stand up additional endpoints on our growing service backbone without having to code the authorization & authentication into every endpoint.

While I understand how to configure OIDC to issue tokens from one endpoint, it's not entirely clear how I would point my other endpoint to the OIDC endpoint for token authentication. Presently JWT authentication and OIDC are simultaneously configured in the middleware 'Configure' method so I'm guessing perhaps on all the subordinate sites I would have a small piece of code in calling app.UseJwtBearerAuthentication simply pointing the JWT middleware to the OIDC endpoint? If this is the case there's still a bit of magic taking place with the app.UseJwtBearerAuthentication that uses OIDC to allow IdentityModel to use HTTP, so I'm not clear if I would need this on the subordinate servers also.

Any advice on how to establish a single OIDC authorization & authentication endpoint and then have 'n' subordinate endpoints point to that endpoint for authentication of JWT tokens would be very much appreciated.


Solution

  • Separating the resource server role (i.e the API) from the authorization server role is definitely possible with ASOS.

    When opting for JWT tokens (instead of the default encrypted tokens), you need to ensure the audience is correctly added to the authentication ticket by calling ticket.SetResources, so the JWT access token gets the appropriate aud claim, containing the identifier associated with your resource server (i.e API):

    public override Task GrantResourceOwnerCredentials(GrantResourceOwnerCredentialsContext context) {
        var identity = new ClaimsIdentity(context.Options.AuthenticationScheme);
        identity.AddClaim(ClaimTypes.NameIdentifier, "[unique identifier]");
    
        var ticket = new AuthenticationTicket(
            new ClaimsPrincipal(identity),
            new AuthenticationProperties(),
            context.Options.AuthenticationScheme);
    
        // Call SetResources with the list of resource servers
        // the access token should be issued for.
        ticket.SetResources("resource_server_1");
    
        // Call SetScopes with the list of scopes you want to grant.
        ticket.SetScopes("profile", "offline_access");
    
        context.Validate(ticket);
    
        return Task.FromResult(0);
    }     
    

    In your API app, you just have to set the options.Audience property with the identifier used in the authorization server, and it should work:

    app.UseJwtBearerAuthentication(new JwtBearerOptions {
        AutomaticAuthenticate = true,
        AutomaticChallenge = true,
        Audience = "resource_server_1",
        Authority = "http://localhost:61854"
    });
    

    I would have a small piece of code in calling app.UseJwtBearerAuthentication simply pointing the JWT middleware to the OIDC endpoint? If this is the case there's still a bit of magic taking place with the app.UseJwtBearerAuthentication that uses OIDC to allow IdentityModel to use HTTP, so I'm not clear if I would need this on the subordinate servers also.

    The JWT bearer middleware automatically retrieves the cryptographic key used to sign the access token from the authorization server mentioned in the options.Authority property, by making an HTTP call to the configuration metadata endpoint: you don't have to configure anything, even if the API project is separated from the authorization server app.