I'm using IdentityServer4 with IdentityServer4.AccessTokenValidation for handling Reference Token.

This is what I have done in Startup.cs:

public void ConfigureServices(IServiceCollection services)
{
     // Add identity server 4.
    services.AddIdentityServer()
        .AddProfileService<IdentityServerProfileService>()
        .AddInMemoryClients(LoadInMemoryIdentityServerClients())
        .AddInMemoryApiResources(LoadInMemoryApiResources())
        .AddInMemoryIdentityResources(LoadInMemoryIdentityResource())
        .AddProfileService<IdentityServerProfileService>()
        .AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
        .AddDeveloperSigningCredential();

    // Add jwt validation.
    services.AddAuthentication(options =>
        {
            options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        })
        .AddIdentityServerAuthentication(options =>
        {
            // base-address of your identityserver
            options.Authority = "https://localhost:44386";

            options.ClaimsIssuer = "https://localhost:44386";

            // name of the API resource
            options.ApiName = "api1";
            options.ApiSecret = "web-api-secret";

            options.RequireHttpsMetadata = false;

        });
}

protected static IEnumerable<Client> LoadInMemoryIdentityServerClients()
{
    var clients = new List<Client>();

    var client = new Client();
    client.ClientId = "web-api-client";
    client.AllowedGrantTypes = GrantTypes.ResourceOwnerPassword;
    client.ClientSecrets = new[] {new Secret("web-api-secret".Sha256())};
    client.AccessTokenType = AccessTokenType.Reference;
    client.AllowedScopes = new[]
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile,
        IdentityServerConstants.StandardScopes.Email,
        IdentityServerConstants.StandardScopes.Address,
        "api1"
    };
    clients.Add(client);

    return clients;
}

protected static IEnumerable<IdentityResource> LoadInMemoryIdentityResource()
{
    //var profileIdentityResource = new IdentityResource("repository-read", "repository-read", new List<string> { "claim-01", "age" });
    return new List<IdentityResource>
    {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
        //profileIdentityResource
    };
}

protected static IEnumerable<ApiResource> LoadInMemoryApiResources()
{
    var apiResources = new List<ApiResource>();
    var apiResource = new ApiResource("api1", "My API");
    apiResource.UserClaims = new[]
    {
        "age"
    };
    apiResources.Add(apiResource);
    return apiResources;
}

When I make a request with structure shown in the below image:

enter image description here

I received a token. After using the received token to make a request to protected api resource api/user/search. It gave me 401 status code.

In visual studio output. This is what I saw:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST http://localhost:56219/api/user/search application/json 5
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 10.9132ms 307 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST https://localhost:44386/api/user/search application/json 5
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST https://localhost:44386/connect/introspect application/x-www-form-urlencoded 143
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Debug: AuthenticationScheme: Bearer was not authenticated.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Debug: AuthenticationScheme: Bearer was not authenticated.
IdentityServer4.Hosting.EndpointRouter:Debug: Request path /connect/introspect matched to endpoint type Introspection
IdentityServer4.Hosting.EndpointRouter:Debug: Endpoint enabled: Introspection, successfully created handler: IdentityServer4.Endpoints.IntrospectionEndpoint
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.IntrospectionEndpoint for /connect/introspect
IdentityServer4.Endpoints.IntrospectionEndpoint:Debug: Starting introspection request.
IdentityServer4.Validation.BasicAuthenticationSecretParser:Debug: Start parsing Basic Authentication secret
IdentityServer4.Validation.PostBodySecretParser:Debug: Start parsing for secret in post body
IdentityServer4.Validation.SecretParser:Debug: Parser found secret: PostBodySecretParser
IdentityServer4.Validation.SecretParser:Debug: Secret id found: api1
IdentityServer4.Validation.HashedSharedSecretValidator:Debug: No shared secret configured for client.
IdentityServer4.Validation.SecretValidator:Debug: Secret validators could not validate secret
IdentityServer4.Validation.ApiSecretValidator:Error: API validation failed.
IdentityServer4.Endpoints.IntrospectionEndpoint:Error: API unauthorized to call introspection endpoint. aborting.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 57.8551ms 401 
IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler:Error: Error returned from introspection endpoint: Unauthorized
IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler:Information: BearerIdentityServerAuthenticationIntrospection was not authenticated. Failure message: Error returned from introspection endpoint: Unauthorized
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Information: Bearer was not authenticated. Failure message: Error returned from introspection endpoint: Unauthorized
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Information: Bearer was not authenticated. Failure message: Error returned from introspection endpoint: Unauthorized
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Information: Bearer was not authenticated. Failure message: Error returned from introspection endpoint: Unauthorized
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executing endpoint 'QrApi.Controllers.UserController.SearchUsersAsync (QrApi)'
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Route matched with {action = "SearchUsersAsync", controller = "User"}. Executing action QrApi.Controllers.UserController.SearchUsersAsync (QrApi)
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization failed.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
Microsoft.AspNetCore.Mvc.ChallengeResult:Information: Executing ChallengeResult with authentication schemes ().
IdentityModel.AspNetCore.OAuth2Introspection.OAuth2IntrospectionHandler:Information: AuthenticationScheme: BearerIdentityServerAuthenticationIntrospection was challenged.
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler:Information: AuthenticationScheme: Bearer was challenged.
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executed action QrApi.Controllers.UserController.SearchUsersAsync (QrApi) in 10.8603ms
Microsoft.AspNetCore.Routing.EndpointMiddleware:Information: Executed endpoint 'QrApi.Controllers.UserController.SearchUsersAsync (QrApi)'
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 135.7991ms 401 

I have found tutorials about reference token, but none of them help me to solve this case.

What am I missing ?

Thank you,

2 Answers

0
Redplane On Best Solutions

Seems to be my configuration is invalid for API Resource.

This is my original setting for API Resources:

protected static IEnumerable<ApiResource> LoadInMemoryApiResources()
{
    var apiResources = new List<ApiResource>();
    var apiResource = new ApiResource("api1", "My API");
    apiResource.UserClaims = new[]
    {
        "age"
    };
    apiResources.Add(apiResource);
    return apiResources;
}

After having added the shared secret key which has been defined in client.ClientSecrets = new[] {new Secret("web-api-secret".Sha256())}; to apiResource:

protected static IEnumerable<ApiResource> LoadInMemoryApiResources()
{
    //...
    var apiResource = new ApiResource("api1", "My API");
    api1Resource.ApiSecrets.Add(new Secret("web-api-secret".Sha256()));
    //...
}

I could make request to protected resources successfully.

Hope this helps someone who is struggling with IdentityServer4 as I did.

0
NIMROD MAINA On

My solution was to add the secret when instantiating the API resource.

protected static IEnumerable<ApiResource> LoadInMemoryApiResources()
{
    var apiResources = new List<ApiResource>();
    var apiResource = new ApiResource("api1", "My API"){
        ApiSecrets = new List<Secret>{
                        new Secret("web-api-secret".Sha256())
                  },
        Scopes = {
                  new Scope("openid")
                 }
    };
    apiResources.Add(apiResource);
    return apiResources;
}