Identityserver4: audience of access_token is not valid

Created on 29 Nov 2017  路  9Comments  路  Source: IdentityServer/IdentityServer4

  • [x] I read and understood how to enable logging

Issue / Steps to reproduce the problem

After successful login at the IdP I get the access_token which I use to get data from an Api. That Api returns a 401 with the below error.

Relevant parts of the log file

 Failed to validate the token  Microsoft.IdentityModel.Tokens.SecurityTokenInvalidAudienceException: IDX10214: Audience validation failed. Audiences: 'http://localhost:50000/resources'. Did not match: validationParameters.ValidAudience: 'myapi' or validationParameters.ValidAudiences: 'null'. 

IdP =>

public static class InMemoryConfiguration
    {

        public static List<TestUser> GetUsers()
        {
            return new List<TestUser>
            {
                new TestUser{ SubjectId = "3ad27793-c0a2-497e-bb13-7c430536921d", IsActive = true,  Username = "[email protected]", Password = "password",
                    Claims = new List<Claim>
                    {
                        new Claim(JwtClaimTypes.Name, "user full name"),
                        new Claim(JwtClaimTypes.BirthDate, new DateTime(1981, 10,11).ToShortDateString()),
                    }
                }
            };
        }

        public static IEnumerable<IdentityResource> GetIdentyResources()
        {
            return new List<IdentityResource>
            {
                // The minimum requirement is, that you provide support for emitting a unique ID for your users - 
                // also called the subject id. This is done by exposing the standard identity resource called openid !
                new IdentityResources.OpenId(), 
                // By requesting scope 'myapi' I get the following claims included in the id_token/profile
                new IdentityResource("myapi", new List<string>{
                      JwtClaimTypes.BirthDate,
                      JwtClaimTypes.Name,
                }),
            };
        }

        public static IEnumerable<ApiResource> GetApiResources()
        {
            return new List<ApiResource>
            {
                new ApiResource("myapi", "display name for api consent screen")
            };
        }

        public static IEnumerable<Client> GetClients()
        {
            return new List<Client>
            {
                new Client
                {
                    AllowAccessTokensViaBrowser = true,
                    ClientId = "my clientid",
                    ClientName = "my client name",
                    AllowedGrantTypes = GrantTypes.Implicit,
                    RedirectUris = { "http://localhost:4200/signin-callback.html", "http://localhost:4200" },
                    AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId,   "myapi"  },
                    AlwaysIncludeUserClaimsInIdToken = true,
                }
            };
        }
    }

Api =>

services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(opt =>
{
    opt.RequireHttpsMetadata = false;
    opt.Authority = "http://localhost:50000"; // IdP
    opt.ApiName = "myapi"; //  api resource name
});

When I check the access_token 'aud' property on jwt.io I get:

access_token =>   "aud": "http://localhost:50000/resources",

AFAIK the aud field in the access_token should identify the Resource Server which is 'myapi' but it is set to "http://localhost:50000/resources"

Why has the access_token.aud property this url?
I have not set that. According to the error the aud field should contain 'myapi'!

I would be gratefull about some advise which I did not get looking into the documentation here:

http://docs.identityserver.io/en/release/search.html?q=aud&check_keywords=yes&area=default#

question

Most helpful comment

Your API Resource name is "myapi" which becomes "aud" - however it looks like you have not defined any scopes under your "myapi" and when you make a request to IdSr4 to get access token - you need to demand scope and based on the demanded scope the API Resource (aud) will appear in the access_token - but if you don't demand any scope which belongs to your "myapi" resource then by default the aud would be - identityserver4 address/resources

Thats why you see the aud: "http://localhost:50000/resources"

All 9 comments

Your API Resource name is "myapi" which becomes "aud" - however it looks like you have not defined any scopes under your "myapi" and when you make a request to IdSr4 to get access token - you need to demand scope and based on the demanded scope the API Resource (aud) will appear in the access_token - but if you don't demand any scope which belongs to your "myapi" resource then by default the aud would be - identityserver4 address/resources

Thats why you see the aud: "http://localhost:50000/resources"

@smuthiya
When I make the request to the IdP for getting the "id_token token" I do it that way:

const settings: any = {
  authority: 'http://localhost:50000',
  client_id: 'my clientid',
  redirect_uri: 'http://localhost:4200/signin-callback.html',
  post_logout_redirect_uri: 'http://localhost:4200',
  response_type: 'id_token token',
  scope: 'openid myapi',
  silent_redirect_uri: 'http://localhost:4200/silent-renew.html',
  loadUserInfo: true
};

As you can see I demanded - as you said denied - the scope 'myapi'.

You're defining two scopes with the same name. You can't do that.

@brockallen Thanks! More and more I understand :-) I changed the IdentityResource("myapi") to IdentityResource("test"), added "test" also to the client.AllowedScopes and asked for it in the JS client settings.scope = "openid myapi test". Then I got my data from the API !

For what it's worth, I have added code to try to alert you of this, but it won't be out until a future release: https://github.com/IdentityServer/IdentityServer4/pull/1733

Have the same issue, IdentityServer4 v2.0.6

private void CreateClients(IApplicationBuilder app)
        {
            using (var serviceScope = app.ApplicationServices.GetService<IServiceScopeFactory>().CreateScope())
            {
                serviceScope.ServiceProvider.GetRequiredService<PersistedGrantDbContext>().Database.Migrate();

                var context = serviceScope.ServiceProvider.GetRequiredService<ConfigurationDbContext>();
                context.Database.Migrate();

                List<Client> clients = new List<Client>
                {
                    new Client
                    {
                        ClientId = "test",
                        AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                        RequireClientSecret = false,
                        AllowedScopes = { "test" }
                    }
                };

                foreach (var client in clients)
                {
                    if (context.Clients.Count(c => c.ClientId == client.ClientId) == 0)
                        context.Clients.Add(client.ToEntity());
                }
                context.SaveChanges();

                // Generate IdentityResources
                List<IdentityResource> identityResources = new List<IdentityResource>
                {
                    new IdentityResources.OpenId(),
                    new IdentityResources.Profile(),
                    new IdentityResources.Email(),
                    new IdentityResources.Address(),
                    new IdentityResources.Phone()
                };

                if (!context.IdentityResources.Any())
                {
                    foreach (var resource in identityResources)
                    {
                        context.IdentityResources.Add(resource.ToEntity());
                    }
                    context.SaveChanges();
                }

                // Generate ApiResources
                List<ApiResource> apiResources = new List<ApiResource>
                {
                    new ApiResource
                    {
                        Name = "test",
                        ApiSecrets =
                        {
                            new Secret("".Sha256())
                        },
                        UserClaims =
                        {
                            JwtClaimTypes.Email,
                            JwtClaimTypes.Audience,
                            JwtClaimTypes.Issuer,
                            JwtClaimTypes.JwtId
                        },
                        Scopes =
                        {
                            new Scope("test")
                        }
                    }
                };

                foreach (var resource in apiResources)
                {
                    if (context.ApiResources.Count(r => r.Name == resource.Name) == 0)
                        context.ApiResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
        }

While curling:

curl -X POST \
  http://localhost:5003/connect/token \
  -H 'Cache-Control: no-cache' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'username=XXX&password=XXX&grant_type=password&client_id=test'

Give me wrong aud field:

{
  "nbf": 1521363999,
  "exp": 1521367599,
  "iss": "http://localhost:5003",
  "aud": [
    "http://localhost:5003/resources",
    "test"
  ],
  "client_id": "test",
  "sub": "37b16c1f-1920-4f4c-a72a-5d5b2fd9cc88",
  "auth_time": 1521363999,
  "idp": "local",
  "email": "XXX",
  "scope": [
    "test"
  ],
  "amr": [
    "pwd"
  ]
}

P.S. Same problem with 2.1.2 as well :(

@brockallen I added scopes to api-resource according your previous answer. Any ideas?

Not sure how exactly it can be avoided since it seems it's hardcoded in DefaultTokenService.CreateAccessTokenAsync

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leksim picture leksim  路  3Comments

garymacpherson picture garymacpherson  路  3Comments

createroftheearth picture createroftheearth  路  3Comments

not-good-with-usernames picture not-good-with-usernames  路  3Comments

eshorgan picture eshorgan  路  3Comments