我正在使用Identity Server 4保护Web API .

如果外部应用程序尝试使用客户端凭据访问它但未传入访问令牌,我会按预期获得未经授权的响应 .

这里的问题是响应不包括我所期望的WWW-Authenticate头,如OAuth spec中所述 .

我在Identity Server中缺少一些配置吗?或者Identity Server实现有问题吗?


相关代码部分如下:

Client registration on 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")
    }
}

ApiResource registration on Identity Server:

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
        }
    }
}

Authentication configuration in the 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";
            });
    (...)
}

Client accessing the 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客户端库就是这样) .