1

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.

Community
  • 1
  • 1
42vogons
  • 683
  • 7
  • 19

1 Answers1

1

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.

Kévin Chalet
  • 39,509
  • 7
  • 121
  • 131
  • Awesome, @Pinpoint - I'll give this a shot. Also should I be referring to AspNet.Security.OpenIdConnect.Server as ASOS in future posts? – 42vogons Dec 28 '15 at 19:25
  • Both are fine, but I guess the long name is better from a SEO perspective ;) FYI, I created a new `aspnet-contrib` tag (this way, I can receive a notification when a question is posted with this tag), so don't hesitate to add it when asking an ASOS question. – Kévin Chalet Dec 28 '15 at 19:41
  • I wired this up to my existing "does it all" implementation so any new APIs can point to my existing endpoint as the resource server. Now the questions ;o) 1) Some time back I began adding the 'resource' key to the token request form with a value set to the URL of the resource server. Is this still required? 2) Also my custom audience validation broke because I was using URI classes to robustly compare 'aud' URI values - can I take this out now with 'aud' being filled in with a free-form name? – 42vogons Dec 28 '15 at 19:57
  • Also what are the scopes for? Thanks! – 42vogons Dec 28 '15 at 20:00
  • Starting with the next beta, the `resource` parameter [will no longer be supported OTB](https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/186), so using it is no longer recommended (calling `ticket.SetResources` is now the official approach). You should still be able to use a URI if you don't like using a string identifier. – Kévin Chalet Dec 28 '15 at 20:10
  • For scope, don't hesitate to take a look at the official definition: https://tools.ietf.org/html/rfc6749#section-3.3. If you don't care about allowing the client applications to retrieve a refresh token or accessing profile claims, you can ignore that. – Kévin Chalet Dec 28 '15 at 20:13
  • FYI, we'll stop using JWT as the default format for access tokens in the next beta: [don't miss this ticket for more information about the different implications](https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/issues/185). – Kévin Chalet Dec 28 '15 at 20:14
  • Is it considered acceptable to have a different audience per environment, e.g. (dev_resource_server_1, test_resource_server_1, prod_resource_server_1)? This seems to work well for us as it disallows using tokens across environments. – 42vogons Dec 28 '15 at 21:17
  • I don't see why it wouldn't be acceptable. You're basically only limited by your imagination :) – Kévin Chalet Dec 28 '15 at 21:40
  • Updated to use the new ASOS beta5 syntax. – Kévin Chalet May 19 '16 at 15:14