Angular中从Duende Identity Server获取客户端令牌失败排查
问题:Angular调用Duende Identity Server获取客户端令牌返回400错误
项目背景
项目采用.NET API服务器、Duende Identity Server认证服务,前端为Angular应用。已实现登录用户可执行全部CRUD操作,未登录用户仅能执行读取操作。原方案在需登录访问的端点添加[Authorize]特性,但存在安全隐患——知晓端点URL的未登录用户仍可访问信息。计划改为未登录用户请求时携带客户端令牌,由API服务器校验后响应读取操作。
该逻辑已在Postman中验证通过,但Angular代码调用时持续返回400状态码(错误信息:invalid_request)。
现有Angular代码
getToken() { const headers = new HttpHeaders(); headers.set('Authorization', `Basic client:secret`); headers.set("Content-Type", "application/json"); const requestOptions = { headers: headers }; let body = { "grant_type": "client_credentials", "client_id": "client", "client_secret": "secret", "scopes": "api", "response_type": "id_token token" } this.http.post("https://localhost:5001/connect/token", body, requestOptions).subscribe( result => { console.log(result); }) }
API服务器CORS配置(Program.cs)
builder.Services.AddCors(options => { options.AddPolicy("AllowAllHeaders", optionsBuilder => { optionsBuilder.AllowAnyOrigin() .AllowAnyHeader() .AllowAnyMethod(); }); });
IdentityServer请求日志
[08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CORS request made for path: /.well-known/openid-configuration from origin: http://localhost:4200 [08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CORS request made for path: /connect/token from origin: http://localhost:4200 [08:50:01 Debug] Duende.IdentityServer.Services.InMemoryCorsPolicyService Client list checked and origin: http://localhost:4200 is allowed [08:50:01 Debug] Duende.IdentityServer.Services.InMemoryCorsPolicyService Client list checked and origin: http://localhost:4200 is allowed [08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CorsPolicyService allowed origin: http://localhost:4200 [08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CorsPolicyService allowed origin: http://localhost:4200 [08:50:01 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: idsrv was not authenticated. [08:50:01 Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP OPTIONS /connect/token responded 204 in 14.3206 ms [08:50:01 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: idsrv was not authenticated. [08:50:01 Debug] Duende.IdentityServer.Hosting.EndpointRouter Request path /.well-known/openid-configuration matched to endpoint type Discovery [08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CORS request made for path: /connect/token from origin: http://localhost:4200 [08:50:01 Debug] Duende.IdentityServer.Hosting.EndpointRouter Endpoint enabled: Discovery, successfully created handler: Duende.IdentityServer.Endpoints.DiscoveryEndpoint [08:50:01 Debug] Duende.IdentityServer.Services.InMemoryCorsPolicyService Client list checked and origin: http://localhost:4200 is allowed [08:50:01 Information] Duende.IdentityServer.Hosting.IdentityServerMiddleware Invoking IdentityServer endpoint: Duende.IdentityServer.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration [08:50:01 Debug] Duende.IdentityServer.Hosting.CorsPolicyProvider CorsPolicyService allowed origin: http://localhost:4200 [08:50:01 Debug] Duende.IdentityServer.Endpoints.DiscoveryEndpoint Start discovery request [08:50:01 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: idsrv was not authenticated. [08:50:01 Information] Duende.IdentityServer.Services.KeyManagement.KeyManager Active signing key found with kid D576C7CF6598DABD3F65CBC5B29D6FD4 for alg RS256. Expires in 83.00:58:45. Retires in 97.00:58:45 [08:50:01 Debug] Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler AuthenticationScheme: idsrv was not authenticated. [08:50:01 Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP GET /.well-known/openid-configuration responded 200 in 35.6220 ms [08:50:01 Debug] Duende.IdentityServer.Hosting.EndpointRouter Request path /connect/token matched to endpoint type Token [08:50:01 Debug] Duende.IdentityServer.Hosting.EndpointRouter Endpoint enabled: Token, successfully created handler: Duende.IdentityServer.Endpoints.TokenEndpoint [08:50:01 Information] Duende.IdentityServer.Hosting.IdentityServerMiddleware Invoking IdentityServer endpoint: Duende.IdentityServer.Endpoints.TokenEndpoint for /connect/token [08:50:01 Warning] Duende.IdentityServer.Endpoints.TokenEndpoint Invalid HTTP request for token endpoint [08:50:01 Information] Serilog.AspNetCore.RequestLoggingMiddleware HTTP POST /connect/token responded 400 in 39.8834 ms
问题排查与修复方案
Angular代码存在以下几个错误点:
1. HttpHeaders不可变,set方法未生效
HttpHeaders是不可变对象,调用set方法会返回新实例,原headers对象未被修改,导致设置的请求头未实际携带。
2. Basic认证凭证未Base64编码
HTTP Basic认证要求凭证(client:secret)必须经过Base64编码后放在Authorization头中,当前代码直接明文传递不符合规范。
3. 请求体格式与字段错误
Duende Identity Server的/connect/token端点要求客户端凭证模式的请求体采用application/x-www-form-urlencoded格式,而非application/json;同时请求体存在字段错误:
scopes应改为单数scoperesponse_type字段属于授权码/隐式流,客户端凭证模式不需要该字段
修复后的完整代码
getToken() { // 构建正确的请求头:Basic凭证编码 + 表单格式 const authCredentials = btoa('client:secret'); const headers = new HttpHeaders() .set('Authorization', `Basic ${authCredentials}`) .set('Content-Type', 'application/x-www-form-urlencoded'); // 构建URL编码的请求体 const body = new HttpParams() .set('grant_type', 'client_credentials') .set('client_id', 'client') .set('client_secret', 'secret') .set('scope', 'api'); this.http.post("https://localhost:5001/connect/token", body, { headers }) .subscribe(result => { console.log(result); }, error => { console.error('获取令牌失败:', error); }); }
额外验证点
- 确认Duende Identity Server中客户端配置开启了
client_credentials授权类型 - 确认客户端的
AllowedScopes包含api - 确保客户端的
ClientSecrets配置与代码中一致
内容的提问来源于stack exchange,提问作者Filip C
相关产品推荐
相关产品推荐

