我正在使用Identity Server 4保护Web API。
如果外部应用尝试使用客户端凭据访问它但未传入访问令牌,我会按预期获得未经授权的响应。
这里的问题是响应不像我期望的那样包含WWW-Authenticate标头,如OAuth spec中所述。
我在Identity Server中缺少一些配置吗?或者Identity Server实现有问题吗?
相关的代码部分如下:
Identity Server上的客户端注册:
new Client()
{
ClientId = "datalookup.clientcredentials",
ClientName = "Data Lookup Client with Client Credentials",
AlwaysIncludeUserClaimsInIdToken = true,
AlwaysSendClientClaims = true,
AllowOfflineAccess = false,
ClientSecrets =
{
new Secret("XXX".Sha256())
},
AllowedGrantTypes = GrantTypes.ClientCredentials,
AllowedScopes =
{
Scopes.DataLookup.Monitoring,
Scopes.DataLookup.VatNumber
},
ClientClaimsPrefix = "client-",
Claims =
{
new Claim("subs", "1000")
}
}
在Identity Server上注册ApiResource:
new ApiResource()
{
Name = "datalookup",
DisplayName = "Data Lookup Web API",
ApiSecrets =
{
new Secret("XXX".Sha256())
},
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Email,
JwtClaimTypes.Profile,
"user-subs"
},
Scopes =
{
new Scope()
{
Name = Scopes.DataLookup.Monitoring,
DisplayName = "Access to the monitoring endpoints",
},
new Scope()
{
Name = Scopes.DataLookup.VatNumber,
DisplayName = "Access to the VAT Number lookup endpoints",
Required = true
}
}
}
Web API中的身份验证配置:
public void ConfigureServices(IServiceCollection services)
{
(...)
services.AddMvc();
services
.AddAuthorization(
(options) =>
{
options.AddPolicy(
Policies.Monitoring,
(policy) =>
{
policy.RequireScope(Policies.Scopes.Monitoring);
});
options.AddPolicy(
Policies.VatNumber,
(policy) =>
{
policy.RequireScope(Policies.Scopes.VatNumber);
policy.RequireClientSubscription();
});
});
services.AddAuthorizationHandlers();
services
.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(
(options) =>
{
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ApiName = "datalookup";
});
(...)
}
访问Web API的客户端:
using (HttpClient client = new HttpClient())
{
// client.SetBearerToken(accessToken);
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, Constants.WebApiEndpoint))
{
using (HttpResponseMessage response = await client.SendAsync(request).ConfigureAwait(false))
{
if (!response.IsSuccessStatusCode)
{
ConsoleHelper.WriteErrorLine(response);
return;
}
string content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ConsoleHelper.WriteInformationLine(content);
}
}
}
请注意,client.SetBearerToken(accessToken)
已被注释,因此我希望响应包含WWW-Authenticate标头。
这背后的全部想法是在客户端库上实现一个功能来处理Http Bearer挑战(例如,Azure KeyVault客户端库就可以了)。