Client credentials flow fails with 401 Not Authorized from the resource server
See original GitHub issueConfirm you’ve already contributed to this project or that you sponsor it
- I confirm I’m a sponsor or a contributor
Version
3.x
Describe the bug
I have a separate authorization server and a resource server. The resource server runs in IIS and have Windows Integrated Security activated too. This means that the resource server should work for both NTLM and bearer tokens (client credentials and authorization code flow).
I am successfully connecting to the auth server, getting an access token and passing it along to the resource server.
However the request is failing with 401 Unauthorized. The request only contains an Authorization
header and there is no Negotiate
header (as it should be) as this request must be done with the bearer token and not with network credentials.
To reproduce
The authorization server is setup as follows:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration.GetConnectionString("Identity"));
options.UseOpenIddict();
});
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = Claims.Name;
options.ClaimsIdentity.UserIdClaimType = Claims.Subject;
options.ClaimsIdentity.RoleClaimType = Claims.Role;
});
services.AddQuartz(options =>
{
options.UseMicrosoftDependencyInjectionJobFactory();
options.UseSimpleTypeLoader();
options.UseInMemoryStore();
});
services.AddQuartzHostedService(options => options.WaitForJobsToComplete = true);
services.AddOpenIddict()
.AddCore(options =>
{
options.UseEntityFrameworkCore().UseDbContext<ApplicationDbContext>();
options.UseQuartz();
})
.AddServer(options =>
{
options.SetAuthorizationEndpointUris("/connect/authorize")
.SetLogoutEndpointUris("/connect/logout")
.SetIntrospectionEndpointUris("/connect/introspect")
.SetTokenEndpointUris("/connect/token")
.SetUserinfoEndpointUris("/connect/userinfo")
.SetVerificationEndpointUris("/connect/verify");
options.AllowAuthorizationCodeFlow() //.RequireProofKeyForCodeExchange()
.AllowPasswordFlow()
.AllowClientCredentialsFlow()
.AllowImplicitFlow()
.AllowHybridFlow()
.AllowRefreshTokenFlow();
options.RegisterScopes(
Scopes.OpenId,
Scopes.Email,
Scopes.Profile,
Scopes.Roles,
Scopes.OfflineAccess);
options.AddEncryptionKey(new SymmetricSecurityKey(Convert.FromBase64String(Configuration["Identity:EncryptionKey"])));
options.AddDevelopmentSigningCertificate();
options.UseAspNetCore()
.EnableStatusCodePagesIntegration()
.EnableAuthorizationEndpointPassthrough()
.EnableLogoutEndpointPassthrough()
.EnableTokenEndpointPassthrough()
.EnableUserinfoEndpointPassthrough()
.EnableVerificationEndpointPassthrough();
options.DisableAccessTokenEncryption();
})
.AddValidation(options =>
{
options.UseLocalServer();
options.UseAspNetCore();
});
});
}
The resource server is configured as follows:
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});
services.AddOpenIddict()
.AddValidation(options =>
{
options.SetIssuer(Configuration["Issuer"]);
options.AddAudiences(Configuration["Audiences"]);
options.UseIntrospection()
.SetClientId(Configuration["ClientId"])
.SetClientSecret(Configuration["ClientSecret"]);
options.UseSystemNetHttp();
options.UseAspNetCore();
});
I’m using Flurl
to get an access token and then connect to a resource endpoint protected with the [Authorize]
attribute. The code is:
//Get access tokens
var tokens = await authServerUrl
.AppendPathSegment("connect/token")
.WithHeader("content-type", "application/x-www-form-urlencoded")
.PostUrlEncodedAsync(new {
grant_type = "client_credentials",
client_id = clientId,
client_secret = clientSecret,
audience = audience,
})
.ReceiveJson<ClientCredentialsResponse>();
//Access protected endpoint
var response= await serverUrl
.AppendPathSegment("api/Protected/Endpoint")
.WithOAuthBearerToken(tokens.AccessToken)
.ReceiveString();
The ClientCredentialsResponse
class is a simple POCO containing access_token
, token_type
and expires_in
.
Exceptions (if any)
No response
Issue Analytics
- State:
- Created a year ago
- Comments:10 (3 by maintainers)
Top GitHub Comments
Oh wow I was using
audience
instead from the Auth0 specification. I do get the audience in the token now!!Then don’t forget to request it: