Duende BFF未授权客户端呼叫身份服务器时错误.授予类型代码更改为授予类型客户端凭据



设置

  • Identity Provider server with Duende server。6、注册客户端使用授权类型代码

    new Client
    {
    ClientId = "test_client",
    RequireClientSecret = false,
    AllowOfflineAccess = true,                      
    ClientName = "Scope",
    AllowedGrantTypes = GrantTypes.Code,
    AllowedScopes = new List<string>
    {
    "openid",
    },
    AllowedCorsOrigins = new List<string>
    {
    "https://localhost:5001",
    "https://localhost:5011", 
    },
    RedirectUris = new List<string> {  "https://localhost:5011/signin-oidc" }
    }
    
  • API与以下配置

    services.Configure<CookiePolicyOptions>(options =>
    {
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.Strict;
    })
    .AddAuthentication(sharedOptions =>
    {
    sharedOptions.DefaultScheme = "smart";
    sharedOptions.DefaultChallengeScheme = "smart";
    })
    .AddPolicyScheme("smart", "Authorization Bearer or OIDC", options =>
    {
    options.ForwardDefaultSelector = context =>
    {
    var authHeader = context.Request.Headers["Authorization"].FirstOrDefault();
    if (authHeader?.StartsWith("Bearer ") == true)
    {
    return JwtBearerDefaults.AuthenticationScheme;
    }
    return "oidc";
    };
    })
    .AddJwtBearer(jwtOptions =>
    {
    jwtOptions.Authority = configuration["Authentication:Authority"];
    jwtOptions.Audience = configuration["Authentication:Audience"];
    jwtOptions.SaveToken = true;
    })
    .AddCookie("Cookies")
    .AddOpenIdConnect("oidc", options =>
    {
    options.SignInScheme = "Cookies";
    options.Authority = configuration["Authentication:Authority"];
    options.ClientId = configuration["Authentication:ClientId"];
    options.ResponseType = "code";
    options.Prompt = "login";
    options.GetClaimsFromUserInfoEndpoint = true;
    options.Scope.Add("openid");
    options.SaveTokens = true;
    });
    services.AddAuthorization(options =>
    {
    options.DefaultPolicy = new AuthorizationPolicyBuilder()
    .RequireAuthenticatedUser()
    .AddAuthenticationSchemes("smart").Build();
    });
    
  • 和一个BFF Webclient配置

    services
    .AddBff()
    .AddRemoteApis();
    services.AddAuthentication(options =>
    {
    options.DefaultScheme = "cookie";
    options.DefaultChallengeScheme = "oidc";
    options.DefaultSignOutScheme = "oidc";
    })
    .AddCookie("cookie", options =>
    {
    options.Cookie.Name = "__Host-blazor";
    options.Cookie.SameSite = SameSiteMode.Strict;
    })
    .AddOpenIdConnect("oidc", options =>
    {
    options.Authority = configuration["Authentication:Authority"];
    // confidential client using code flow + PKCE
    options.ClientId = configuration["Authentication:ClientId"];
    options.ResponseType = "code";
    options.ResponseMode = "query";
    options.MapInboundClaims = false;
    options.GetClaimsFromUserInfoEndpoint = true;
    options.SaveTokens = true;
    // request scopes + refresh tokens
    options.Scope.Clear();
    options.Scope.Add("openid");
    options.Scope.Add("offline_access");
    });
    //services.AddAccessTokenManagement();
    services.AddClientAccessTokenHttpClient(AuthorizedClient, configureClient: client =>
    {
    //This is the address of the Mono API
    client.BaseAddress = new Uri(configuration["ApiConfig:BaseAddress"]);
    });
    

每当我们手动调用API时,我们都会使用一个命名的http客户端,该客户端由httpfactory创建,并附带访问令牌。访问令牌是从HttpContext中收集的。用户在使用端点之前需要登录,所以访问令牌在HttpContext中是有效的。

public BaseHttpClient(IHttpClientFactory httpClientFactory, IHttpContextAccessor httpContextAccessor, ILogger logger)
{
this.httpClient = httpClientFactory.CreateClient(AuthorizedClient);
this.httpContextAccessor = httpContextAccessor;
this.logger = logger;
}
protected async Task<HttpResponseMessage> SendAuthenticatedAsync(HttpRequestMessage request)
{
try
{
var token = await this.httpContextAccessor.HttpContext.GetUserAccessTokenAsync();
this.httpClient.SetBearerToken(token);
var responseMessage = await this.httpClient.SendAsync(request);
return responseMessage;
}
catch (Exception e)
{
this.logger?.Error(e, "Exception at sending the authenticated client");
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.BadRequest
};
}
}

从BFF到API的手动调用成功通过,但是当我们检查日志时,有些事情是不对的。

Duende BFF logs:

[19:05:39 DBG] AuthenticationScheme: cookie was successfully authenticated.
[19:05:39 DBG] AuthenticationScheme: cookie was successfully authenticated.
[19:05:39 INF] Start processing HTTP request POST https://localhost:5001/api/v1/property
[19:05:39 INF] Start processing HTTP request POST https://localhost:5001/api/v1/property
[19:05:39 DBG] Cache miss for access token for client: default
[19:05:39 DBG] Cache miss for access token for client: default
[19:05:39 DBG] Requesting client access token for client: default
[19:05:39 DBG] Requesting client access token for client: default
[19:05:39 DBG] Constructing token client configuration from OpenID Connect handler.
[19:05:39 DBG] Constructing token client configuration from OpenID Connect handler.
[19:05:39 DBG] Returning token client configuration for client: default
[19:05:39 DBG] Returning token client configuration for client: default
[19:05:39 INF] Start processing HTTP request POST https://localhost:5443/connect/token
[19:05:39 INF] Start processing HTTP request POST https://localhost:5443/connect/token
[19:05:39 INF] Sending HTTP request POST https://localhost:5443/connect/token
[19:05:39 INF] Sending HTTP request POST https://localhost:5443/connect/token
[19:05:39 INF] Received HTTP response headers after 160.5943ms - 400
[19:05:39 INF] Received HTTP response headers after 160.5943ms - 400
[19:05:39 INF] End processing HTTP request after 168.1572ms - 400
[19:05:39 INF] End processing HTTP request after 168.1572ms - 400
**HERE**
[19:05:39 ERR] Error requesting access token for client default. Error = unauthorized_client. Error description = null
[19:05:39 ERR] Error requesting access token for client default. Error = unauthorized_client. Error description = null
[19:05:39 INF] Sending HTTP request POST https://localhost:5001/api/v1/property
[19:05:39 INF] Sending HTTP request POST https://localhost:5001/api/v1/property
[19:05:39 INF] Received HTTP response headers after 110.934ms - 400
[19:05:39 INF] Received HTTP response headers after 110.934ms - 400
[19:05:39 INF] End processing HTTP request after 295.8003ms - 400
[19:05:39 INF] End processing HTTP request after 295.8003ms - 400
[19:05:39 INF] Executing StatusCodeResult, setting HTTP status code 200
[19:05:39 INF] Executing StatusCodeResult, setting HTTP status code 200

未授权客户端出现错误

Identity Server日志如下:

[19:05:39 VRB] Calling into client configuration validator: Duende.IdentityServer.Validation.DefaultClientConfigurationValidator
[19:05:39 DBG] client configuration validation for client test_client succeeded.
[19:05:39 DBG] Public Client - skipping secret validation success
[19:05:39 DBG] Client validation success
[19:05:39 INF] {"ClientId": "test_client", "AuthenticationMethod": "NoSecret", "Category": "Authentication", "Name": "Client Authentication Success", "EventType": "Success", "Id": 1010, "Message": null, "ActivityId": "0HMI8JMB87769:00000004", "TimeStamp": "2022-06-07T16:05:39.0000000Z", "ProcessId": 21368, "LocalIpAddress": "::1:5443", "RemoteIpAddress": "::1", "$type": "ClientAuthenticationSuccessEvent"}
[19:05:39 VRB] Calling into token request validator: Duende.IdentityServer.Validation.TokenRequestValidator
[19:05:39 DBG] Start token request validation
[19:05:39 DBG] Start client credentials token request validation
**HERE**
[19:05:39 ERR] Client not authorized for client credentials flow, check the AllowedGrantTypes setting{"clientId": "test_client"}, details: {"ClientId": "test_client", "ClientName": "Scope", "GrantType": "client_credentials", "Scopes": null, "AuthorizationCode": "********", "RefreshToken": "********", "UserName": null, "AuthenticationContextReferenceClasses": null, "Tenant": null, "IdP": null, "Raw": {"grant_type": "client_credentials", "client_id": "test_client"}, "$type": "TokenRequestValidationLog"}
[19:05:39 INF] {"ClientId": "test_client", "ClientName": "Scope", "RedirectUri": null, "Endpoint": "Token", "SubjectId": null, "Scopes": null, "GrantType": "client_credentials", "Error": "unauthorized_client", "ErrorDescription": null, "Category": "Token", "Name": "Token Issued Failure", "EventType": "Failure", "Id": 2001, "Message": null, "ActivityId": "0HMI8JMB87769:00000004", "TimeStamp": "2022-06-07T16:05:39.0000000Z", "ProcessId": 21368, "LocalIpAddress": "::1:5443", "RemoteIpAddress": "::1", "$type": "TokenIssuedFailureEvent"}
[19:05:39 VRB] Invoking result: Duende.IdentityServer.Endpoints.Results.TokenErrorResult
[19:05:39 DBG] Connection id "0HMI8JMB87769" completed keep alive response.
[19:05:39 DBG] 'ConfigurationDbContext' disposed.

我们看到一个请求是用授权类型客户端凭证向身份服务器发出的,但是Duende BFF是用授权类型代码注册的。

输入的客户端发出的http请求通过,因为附加的访问令牌是有效的,但是BFF和IDP的日志记录行为是奇怪的。

有什么想法或线索可能导致最好的朋友给IDP打这样的电话吗?

找到问题。我将命名的http客户端注册为AddClientAccessTokenHttpClient

services.AddClientAccessTokenHttpClient(AuthorizedClient, configureClient: client =>
{
//This is the address of the Mono API
client.BaseAddress = new Uri(configuration["ApiConfig:BaseAddress"]);
});

导致它发出的请求将授予类型设置为Client Credentials。

修复是注册到正确的http客户端- AddUserAccessTokenHttpClient

services.AddUserAccessTokenHttpClient(AuthorizedClient, configureClient: client =>
{
//This is the address of the Mono API
client.BaseAddress = new Uri($"{configuration["ApiConfig:BaseAddress"]}");
});

现在请求的授权类型设置为code。

相关内容

  • 没有找到相关文章

最新更新