IdentityServer4 Reference Access Token Sometimes being transmitted as JWT Token instead

Viewed 446

We have just upgraded our web app to .NET 5 and IdentityServer4 to V4, also we switched from hybrid flow to code + PKCE. The client is set to an access token type of reference, also the client uses bearer tokens for an internal API as well as cookies for the main website.

When we deploy to our internal development server (IIS 8.5) or to Azure App Services randomly when we ask for the access token (reference) we are returned an access token (JWT) instead. We did use the httpContext.GetTokenAsync() method but then replaced it with the identitymodel.aspnetcore GetUserAccessTokenAsync() method but it still returns a JWT Token.

I have validated the JWT tokens contents and they are the relevant user and their claims. I have also checked the persisted grant table and the reference token entered in there specifies it as a JWT instead of Reference.

The only way to rectify the situation is

  1. Stop the website and identityserver
  2. Clear the cookies in the browser
  3. Delete all entries in the persisted grants table
  4. Recycle the app pools
  5. Start the identity server and then perform a login
  6. Start the website which the login is for and suddenly we get a reference access token again

Identity Server Client Config

                AllowedGrantTypes = 
                {
                    GrantType.AuthorizationCode,
                    "exchange_reference_token"
                },
                AccessTokenType = AccessTokenType.Reference,
                AccessTokenLifetime = 86400,

                RequireConsent = false,
                AllowAccessTokensViaBrowser = true,

                ClientSecrets =
                {
                    new Secret("*******)
                },

                RedirectUris = { $"{client}/signin-oidc" },
                PostLogoutRedirectUris = { $"{client}/signout-callback-oidc" },
                FrontChannelLogoutUri = $"{client}/signout-oidc",

                AllowedCorsOrigins = { client },

                AllowedScopes =
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "roles",
                    "API1",
                    "API2",
                    "API3",
                    "Signalr"
                },
                UpdateAccessTokenClaimsOnRefresh = true,
                AllowOfflineAccess = true
1 Answers

I've found out what is causing the issue. We are using signalr in the website and we are having to use the exchange_reference_token, however in the exchange code it forces the access token to JWT, this then gets inserted in the persisted grant table causing any future request to return JWT instead of of reference access token

public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
    var referenceToken = context.Request.Raw.Get("token");

    if (referenceToken == null)
    {
        context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Missing Reference Token");
    }

    var result = await _validator.ValidateAccessTokenAsync(referenceToken);
    if (result == null)
    {
        context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Invalid Reference Token");
    }

    // Generate the JWT as if it was for the reference token's client           
    context.Request.Client.AccessTokenType = AccessTokenType.Jwt;

    var sub = result.Claims.FirstOrDefault(c => c.Type == "sub").Value;

    context.Result = new GrantValidationResult(sub, GrantType, result.Claims);
}
Related