title: Unable to Validate JWT or access_token in API with IdentityServer4
body: <p>I'm working on building out some new functionality where we have an existing system. Our goal is to have different Angular SPA's communicate with APIs to present a new interface for our legacy app. A team has set up IdentityServer4 to use the legacy application for credentials verification. </p>
  
  <p>The SPA, written in Angular 7, uses the implicit flow to authenticate. <em>Response type is "token id_token"</em> I've been able to get this flow to work insomuch as I can have an unauthenticated user come to the site, get kicked over to the IdentityServer authentication screens, log in with correct credentials, and then redirected back to the app. When I get the token back the claims object has a single value for <code>aud</code>, the client_id for the SPA, for example, <em>spa-client</em>. The access_token and id_token are both a JWT with RS256 as the signature method. Lastly, I've created an interceptor in the SPA to send the JWT (in actuality it just uses whatever value is sent back as the access_token) as the "Bearer" token on API requests.</p>
  
  <p>I have now created a .NET Core 2.2 WebApi app to build out a service API to be consumed by the SPA. This app uses the IdentityServer4.AccessTokenValidation package to "protect" the API <em>I've read the section in the IdentityServer4 docs.</em> In my <code>ConfigureServices</code> method I've written the following to configure auth:</p>
  
  <pre class="lang-cs prettyprint-override"><code>services    
      .AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
      .AddIdentityServerAuthentication(options =&gt;
      {
          if (string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.Authority)) throw new ConfigurationException("", nameof(serviceConfiguration.Authentication.Authority), "An authority must be defined.");
          if (string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.ApiName)) throw new ConfigurationException("", nameof(serviceConfiguration.Authentication.ApiName), "An api name must be defined.");
  
          options.Authority = serviceConfiguration.Authentication.Authority;
          options.ApiName = serviceConfiguration.Authentication.ApiName;
  
          if (!string.IsNullOrWhiteSpace(serviceConfiguration.Authentication.ApiSecret))
          {
              options.ApiSecret = serviceConfiguration.Authentication.ApiSecret;
          }
  
          options.RequireHttpsMetadata = false;
          options.SupportedTokens = SupportedTokens.Both;
      }
  </code></pre>
  
  <p><code>serviceConfiguration</code> is a strongly type representation of my JSON appsettings.json</p>
  
  <p>My understanding is that with <code>options.SupportedTokens = SupportedTokens.Both</code> will check the JWT and do introspection. </p>
  
  <p>Now that all this is setup I've taken the simple example <code>ValuesController</code> that is created by the API template and added the <code>[Authorize]</code> attribute to it. Doing a simple GET request to this endpoint in Postman returns a 401 as I would expect. </p>
  
  <p>Next I try logging into the SPA and going to a test page which will do the same but with the interceptor will have the bearer token, the access_token, or basically the JWT. I still get a 401 from the service.</p>
  
  <p>Looking at the logs output by the service I don't see anything specific that is helpful:</p>
  
  <blockquote>
    <p>info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
          Request starting HTTP/1.1 OPTIONS <a href="http://localhost:5001/api/values" rel="nofollow noreferrer">http://localhost:5001/api/values</a> info:
    Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
          Request starting HTTP/1.1 OPTIONS <a href="http://localhost:5001/api/values" rel="nofollow noreferrer">http://localhost:5001/api/values</a> info:
    Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
          CORS policy execution successful. info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
          CORS policy execution successful. info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 61.1352ms 204 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 61.1272ms 204 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
          Request starting HTTP/1.1 GET <a href="http://localhost:5001/api/values" rel="nofollow noreferrer">http://localhost:5001/api/values</a> application/json info:
    Microsoft.AspNetCore.Hosting.Internal.WebHost[1]
          Request starting HTTP/1.1 GET <a href="http://localhost:5001/api/values" rel="nofollow noreferrer">http://localhost:5001/api/values</a> application/json info:
    Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
          CORS policy execution successful. info: Microsoft.AspNetCore.Cors.Infrastructure.CorsService[4]
          CORS policy execution successful. info: Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
          Executing endpoint 'Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web)' info:
    Microsoft.AspNetCore.Routing.EndpointMiddleware[0]
          Executing endpoint 'Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web)' info:
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
          Route matched with {action = "Get", controller = "Values"}. Executing action
    Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web) info:
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[1]
          Route matched with {action = "Get", controller = "Values"}. Executing action
    Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web) info:
    Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
          Authorization failed. info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
          Authorization failed. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
          Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. info:
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[3]
          Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'. info:
    Microsoft.AspNetCore.Mvc.ChallengeResult[1]
          Executing ChallengeResult with authentication schemes (). info: Microsoft.AspNetCore.Mvc.ChallengeResult[1]
          Executing ChallengeResult with authentication schemes (). info: IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[12]
          AuthenticationScheme: Bearer was challenged. info: IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler[12]
          AuthenticationScheme: Bearer was challenged. info: Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
          Executed action Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web) in 166.3038ms info:
    Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker[2]
          Executed action Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web) in 162.8827ms info:
    Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
          Executed endpoint 'Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web)' info:
    Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
          Executed endpoint 'Examples.TestApp.API.Web.Controllers.ValuesController.Get
    (Examples.TestApp.API.Web)' info:
    Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 459.6677ms 401 info: Microsoft.AspNetCore.Hosting.Internal.WebHost[2]
          Request finished in 473.7648ms 401</p>
  </blockquote>
  
  <p>Setting the logging to <em>Trace</em> didn't reveal anything else. </p>
  
  <p>I did try going to Postman to see if I could do introspection on the token manually. I set up authorization to basic and set the Username to "test-api", the api id, and the password that I set in its client secret. In the body a set it to x-www-form-urlencoded with a key of "token" and the value the access_token. I get a 200 OK with claims along with "active: true". Seems like that should be good, right?</p>
  
  <p>I don't own the IdentityServer implementation so I'm not quite sure what's going on at that end except for being able to log into the admin UI. I've logged into the admin UI for the identity server and double checked the settings for the client and the API. The client gets granted a "api" scope and the API gets the same scope "api". This is shown in the scope that is sent back in the introspection response as well as the token that I get from the client.</p>
  
  <p>I've also tried setting the <code>options.SupportedTokens = SupportedTokens.Reference</code> and <code>options.SupportedTokens = SupportedTokens.JWT</code>. Neither had any change. :(</p>
  
  <p>What am I doing wrong? Is this a configuration issue or me being an idiot (probably the latter).</p>
  
  <p><strong>EDIT</strong>: RE access_token and id_token</p>
  
  <p>I was incorrect in the original question. The tokens are different (I need to look at more than just the header portion).</p>
  
  <p>The audiences are weird. I've never paid attention to a possible difference here when decoding. My powers of perception must suck!</p>
  
  <p>The access token has aud: "http:///resources"
  The id_token has aud: "spa-client"</p>