Reputation: 162
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
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
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