Ryan D
Ryan D

Reputation: 162

Why is the access token generated by IdentityServer4 always invalid?

I am trying to setup a fairly basic identity oAuth server using IdentityServer4 on asp.net core 3.1. I've created a basic project and using inmemory configuration.

I can request and receive a bearer token (client credentials flow), but whenever I make an introspection call, I receive a 401 response. I suspect it has something to do with the token itself - trying to verify it using some of the online websites always seems to show an invalid token.

My code, Starup.cs:

            services.AddIdentityServer()
                .AddInMemoryApiScopes(InMemoryConfig.GetApiScopes())
                .AddInMemoryApiResources(InMemoryConfig.GetApiResources())
                .AddInMemoryIdentityResources(InMemoryConfig.GetIdentityResources())
                .AddTestUsers(InMemoryConfig.GetUsers())
                .AddInMemoryClients(InMemoryConfig.GetClients())
                .AddDeveloperSigningCredential(); 

InMemoryConfig.cs:

        public static IEnumerable<Client> GetClients() =>
            new List<Client>
            {
               new Client
               {
                    ClientId = "myclient",
                    ClientSecrets = new [] { new Secret("dev-$he turned me into a newt!".Sha512()) },
                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId,
                        "read",
                        "write",
                        "update",
                        "delete"}
                }
            };


        public static IEnumerable<ApiScope> GetApiScopes() =>
            new List<ApiScope> { 
                new ApiScope("read", "read data"),
                new ApiScope("write", "write data"),
                new ApiScope("update", "update data"),
                new ApiScope("delete", "delete data")
            };

        public static IEnumerable<ApiResource> GetApiResources() =>
            new List<ApiResource>
            {
                new ApiResource("partnerservices", "Partner Services")
                {
                    ApiSecrets = new [] {new Secret("PSSecret")},
                    Scopes = { "read","write","update","delete"}
                },
                new ApiResource("coreservices", "Core Services")
                {
                    Scopes = { "read","write","update","delete"}
                },
                new ApiResource("mysite", "My Site")
                {
                    Scopes = { "read","write","update","delete"}
                }

            };

Here is the token I obtained:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjEwQkZDNjQ5NTM2NkU0NTc2NjlDNkEzN0VBOTczQjAwIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MDQ3MDA0NjAsImV4cCI6MTYwNDcwNDA2MCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTEiLCJhdWQiOlsicGFydG5lcnNlcnZpY2VzIiwiY29yZXNlcnZpY2VzIiwibXlzaXRlIl0sImNsaWVudF9pZCI6Im15Y2xpZW50IiwianRpIjoiNjYyOTIzQjNGQkY4QzU4QTg2OTBCM0NFNjkxNzZFQTkiLCJpYXQiOjE2MDQ3MDA0NjAsInNjb3BlIjpbImRlbGV0ZSIsInJlYWQiLCJ1cGRhdGUiLCJ3cml0ZSJdfQ.Troia38tgbAR18VMC47UosniAVW8Iq6PwtksX2bOeEClm42_koYsw8_JBZ97i9nk5b9W0liGsQO0AEgukiNIDbeihHI4zMBGSM2y3ZJw-09g7mttbRVFIgPuD_4u7bJi57zZLTdAF6jg_9vxHhHbp2aAH7uXLMgsZB8qpCH9_hJnoxd5r8OKKFBL9wlOSYtnh-D8a0bKH_VnTZxn28Ozt4NJ06PxuqvcC_GEnAHmbSajeDGtPkIhvGhTU2Nd7nHtv9EvBoS__wXsvefgSpv-yCaGZMFc58Abv_LO_WqHsSTbl8eayk6ayoVShiWYh_5Ei5gVmcyCQAF1SO1X8qkGJw",
  "expires_in": 3600,
  "token_type": "Bearer",
  "scope": "delete read update write"
}

...and here is the public key info returned from "jwks_uri": "https://localhost:44311/.well-known/openid-configuration/jwks in my discover docs:

{"keys":[{"kty":"RSA","use":"sig","kid":"10BFC6495366E457669C6A37EA973B00","e":"AQAB","n":"0Zq2wamtiFtqhNNqlY4rYyC1nbO1hB1ztXIhYJc7tjhhpsyViXyWlKiD8cORmHieO8sH4ZSXCeQbYV7u1nXI7SG28ul9kozpUO7M9vf1nOv50o9mZg_BOyMFnTgcDMN6zjT1IWM9K5NY-2D7jSZvxQo4GNwWr2SpytRXLYAgWSHgj3wDarZIXfHKmIRSYvS8L6d_2G0dbxWD699VQMRz0WBjRR6qwhlXc4-4dSeBfvrioWf0DA6LD2NGNA1oP1XWuV_Htkh2Ay5Ck4AyUc3xbC4TVI3SpuhYMK_3zuHwmnzA1L4SyXnHG963hXpbDSUtu01i3YTK2v5MterR9PFWzQ","alg":"RS256"}]}

If you're still reading this, thanks already! The issue is that I cannot make a subsequent introspect call using that bearer token, nor can I validate that token using any of the online validators (e.g. jwt.io)

What am I missing?

for completeness, here is my introspect call:

> POST /connect/introspect HTTP/2
> Host: localhost:44311
> authorization: Basic ZXRyYWMtY2xhcmlmeTpldHJhY2Rldi0kaGUgdHVybmVkIG1lIGludG8gYSBuZXd0IQ==
> user-agent: insomnia/2020.4.2
> content-type: application/x-www-form-urlencoded
> accept: */*
> content-length: 796

| token=eyJhbGciOiJSUzI1NiIsImtpZCI6IjEwQkZDNjQ5NTM2NkU0NTc2NjlDNkEzN0VBOTczQjAwIiwidHlwIjoiYXQrand0In0.eyJuYmYiOjE2MDQ2ODkxMTgsImV4cCI6MTYwNDY5MjcxOCwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6NDQzMTEiLCJhdWQiOlsiZXRyYWMtcGFydG5lcnNlcnZpY2VzIiwiZXRyYWMtY29yZXNlcnZpY2VzIiwiZXRyYWMtbGlua2QiXSwiY2xpZW50X2lkIjoiZXRyYWMtY2xhcmlmeSIsImp0aSI6IjNGQzQ0NzE4QTAyMzcyRUFBOUFDMDUzOEQyNjQyMDk5IiwiaWF0IjoxNjA0Njg5MTE4LCJzY29wZSI6WyJkZWxldGUiLCJyZWFkIiwidXBkYXRlIiwid3JpdGUiXX0.iDl8MEgqRcgFdg3T-rST9jTIxGJfwMtGQ-a6nSAxOadqs8P0EM_UcCYVwKhuzjNM4uORbrhsD5XDB2wMtgMEaSgNdKlH5NA2OTRkftFAKyii2M0ihQL06rN1KVURKySK4d38pezuQxZ48blDaro5ae8RUxMOkRlsPwX6e6LBKeJ2MkqmKcj6AJ1b0sDkJpdgagy4gjvBGWcQOAf_TuEobaWBEKEdzF_gM0a321PDFayCTuezc7bIYN5eq4VQL3-PINbOq72Cashjmi4BCNF0sV-gKq1blDMEZ6UcN-jGOUaaEtvh36H8bHw2H-mY1535tPpX4PzpE_zB6mtHvpJScA

Upvotes: 1

Views: 1476

Answers (2)

d_f
d_f

Reputation: 4859

Regarding the validation with jwt.io. Actually the result is Signature Verified. Most likely you entered the keys array, not the only element into the Public key field.

Next, you do not have to use introspection for JWTs. They are for validation in place. Please read the first googled SO answer for more details.

Upvotes: 0

Tore Nestenius
Tore Nestenius

Reputation: 19901

It all looks good, the code I use to make manual introspection calls looks like this below using the Flurl.Http NuGetp package:

/// <summary>
/// Introspect an access token
/// </summary>
/// <param name="accessToken">The received access token</param>
/// <param name="apiName">The name of this API-Resource as defined in Identity Server</param>
/// <param name="apiSecret">the API secret as defined in the API-Resource</param>
private void IntrospectToken(string accessToken, string apiName, string apiSecret)
{
    //Get the Introspection endpoint URL

    var url = new Url(openIdConfig.IntrospectionEndpoint).WithBasicAuth(apiName, apiSecret);

    var introspectionResult = url.PostUrlEncodedAsync(new
    {
        token = accessToken

    }).ReceiveString().Result;

    //Console.WriteLine("\r\nintrospection Result");
    //Console.WriteLine(introspectionResult);
}

Perhaps it can give some clues?

Upvotes: 1

Related Questions