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.
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#
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.
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"