具有个人帐户和 ASP.NET 核心托管的 Blazor WebAssembly 应用程序 - 令牌请求 - "error":"unauthorized_client"
Blazor WebAssembly App with Individual Accounts and ASP.NET Core Hosted - Token request - "error": "unauthorized_client"
正在创建一个新的 Blazor WebAssembly App
和 Microsoft Visual Studio 2019 Version 16.9.4
以及这些规格:Target Framework .NET 5.0
、Authentication Type Individual Accounts
和 ASP.NET Core Hosted
:
在版本 5.0.5
:
中提供具有这些 NuGet
的服务器项目
- Microsoft.AspNetCore.ApiAuthorization.IdentityServer
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.AspNetCore.Identity.UI
Startup.cs 包含此代码:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
阅读 Microsoft 的博客 post ASP.NET Core Authentication with IdentityServer4
我应该能够通过如下所示的示例请求检索令牌:
POST /connect/token HTTP/1.1
Host: localhost:5000
Cache-Control: no-cache
Postman-Token: 958df72b-663c-5638-052a-aed41ba0dbd1
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=Mike%40Contoso.com&password=MikesPassword1!&client_id=myClient&scope=myAPIs
https://devblogs.microsoft.com/aspnet/asp-net-core-authentication-with-identityserver4/
创建看起来像这样的请求,但针对创建的解决方案:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 153
grant_type=password&username=example%40example.com&password=Password1&client_id=WebApplication4.Client&scope=WebApplication4.ServerAPI%20openid%20profile
此请求 returns HTTP 状态 400 请求正文错误:
{
"error": "unauthorized_client"
}
我很确定这些值是正确的,因为我从用于登录 Web 应用程序的请求中获得了 client_id
和 scope
。但是,该流程不使用 grant_type=password
。来自登录的示例请求:
https://localhost:44388/Identity/Account/Login?ReturnUrl=/connect/authorize/callback?client_id=WebApplication4.Client&redirect_uri=https%3A%2F%2Flocalhost%3A44388%2Fauthentication%2Flogin-callback&response_type=code&scope=WebApplication4.ServerAPI%20openid%20profile&state=12345&code_challenge=12345&code_challenge_method=S256&response_mode=query
确认用户存在并在工作:
我错过了什么?
TLDR:
从 appsettings.json
中删除:
"Clients": {
"WebApplication4.Client": {
"Profile": "IdentityServerSPA"
}
}
编辑Startup.cs
:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
//Use Configuration.GetSection("MySecretValue").Value; to get a value from appsettings.json
ClientSecrets = { new Secret("MySecretValue".Sha256()) },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
此请求有效:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 168
grant_type=password&username=example%40example.com&password=Password1!&client_id=WebApplication4.Integration&scope=WebApplication4.ServerAPI&client_secret=MySecretValue
长答案:
我开始尝试使用日志记录获得更好的错误消息。
我将下面的代码添加到 public static IHostBuilder CreateHostBuilder(string[] args)
中
Program.cs
:
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0
调试时,我可以在发出请求时显示服务器应用程序的输出。它看起来像这样:
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenEndpoint for /connect/token
info: IdentityServer4.Events.DefaultEventService[0]
{
"ClientId": "WebApplication4.Client",
"AuthenticationMethod": "NoSecret",
"Category": "Authentication",
"Name": "Client Authentication Success",
"EventType": "Success",
"Id": 1010,
"ActivityId": "8000000a-0000-8f00-b63f-84710c7967bb",
"TimeStamp": "2021-04-29T11:47:07Z",
"ProcessId": 8436,
"LocalIpAddress": "::1:44388",
"RemoteIpAddress": "::1"
}
fail: IdentityServer4.Validation.TokenRequestValidator[0]
Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = WebApplication4.Client }, details: {
"ClientId": "WebApplication4.Client",
"ClientName": "WebApplication4.Client",
"GrantType": "password",
"Raw": {
"grant_type": "password",
"username": "example@example.com",
"password": "***REDACTED***",
"client_id": "WebApplication4.Client",
"scope": "WebApplication4.ServerAPI"
}
}
info: IdentityServer4.Events.DefaultEventService[0]
{
"ClientId": "WebApplication4.Client",
"ClientName": "WebApplication4.Client",
"Endpoint": "Token",
"GrantType": "password",
"Error": "unauthorized_client",
"Category": "Token",
"Name": "Token Issued Failure",
"EventType": "Failure",
"Id": 2001,
"ActivityId": "8000000a-0000-8f00-b63f-84710c7967bb",
"TimeStamp": "2021-04-29T11:47:07Z",
"ProcessId": 8436,
"LocalIpAddress": "::1:44388",
"RemoteIpAddress": "::1"
}
要查看的错误消息是 Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = WebApplication4.Client }
。
通过这个错误消息我发现了这个问题:
Question about ASP.NET Core 3 Identity / Identity Server / SPA support for Resource Owner Password Grant Type
在那里我可以阅读
found that the allowed grant type of password was not being added when
the profile is set to IdentityServerSPA.
查看 appsettings.json
应用程序使用该配置文件:
"IdentityServer": {
"Clients": {
"WebApplication4.Client": {
"Profile": "IdentityServerSPA"
}
}
},
看看 Microsoft Application profiles
它实际上是这样的:
- redirect_uri默认为/authentication/login-callback。
- post_logout_redirect_uri 默认为
/authentication/logout-callback.
- 范围集包括openid、配置文件和每个范围
为应用程序中的 API 定义。
- 允许的 OIDC 响应类型集是 id_token 令牌或每个
他们分别 (id_token, token).
- 允许的响应模式是片段。
在开始修改之前,我访问了 URL https://localhost:44388/.well-known/openid-configuration
以获取当前配置。它看起来像这样,具体说 grant_types_supported: ...password
:
{
"issuer": "https://localhost:44388",
"jwks_uri": "https://localhost:44388/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://localhost:44388/connect/authorize",
"token_endpoint": "https://localhost:44388/connect/token",
"userinfo_endpoint": "https://localhost:44388/connect/userinfo",
"end_session_endpoint": "https://localhost:44388/connect/endsession",
"check_session_iframe": "https://localhost:44388/connect/checksession",
"revocation_endpoint": "https://localhost:44388/connect/revocation",
"introspection_endpoint": "https://localhost:44388/connect/introspect",
"device_authorization_endpoint": "https://localhost:44388/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"openid",
"profile",
"WebApplication4.ServerAPI",
"offline_access"
],
"claims_supported": [
"sub",
"name",
"family_name",
"given_name",
"middle_name",
"nickname",
"preferred_username",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true
}
由于某些原因 IdentityServer
Clients
无法在代码和 appsettings.json
中进行配置。因此,我从 appsettings.json
中删除了 Clients
并将其添加到 Startup.cs:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
没有 WithRedirectUri
和 WithLogoutRedirectUri
它不起作用,OidcConfigurationController
得到了 ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId);
和 System.InvalidOperationException: 'Sequence contains no elements'
的异常。出于某种原因,这在使用 appsettings.json
.
时会自动修复
我现在在发帖到 /connect/token
时收到错误消息:
{
"error": "invalid_client"
}
但是我在日志中得到了一个更好的错误:
Invalid client configuration for client WebApplication4.Integration: Client secret is required for password, but no client secret is configured.
向 Startup.cs
添加了一个秘密:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
//Use Configuration.GetSection("MySecretValue").Value; to get a value from appsettings.json
ClientSecrets = { new Secret("MySecretValue".Sha256()) },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
请求:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 168
grant_type=password&username=example%40example.com&password=Password1!&client_id=WebApplication4.Integration&scope=WebApplication4.ServerAPI&client_secret=MySecretValue
终于成功了,正常的登录流程也成功了!
正在创建一个新的 Blazor WebAssembly App
和 Microsoft Visual Studio 2019 Version 16.9.4
以及这些规格:Target Framework .NET 5.0
、Authentication Type Individual Accounts
和 ASP.NET Core Hosted
:
在版本 5.0.5
:
NuGet
的服务器项目
- Microsoft.AspNetCore.ApiAuthorization.IdentityServer
- Microsoft.AspNetCore.Identity.EntityFrameworkCore
- Microsoft.AspNetCore.Identity.UI
Startup.cs 包含此代码:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
阅读 Microsoft 的博客 post ASP.NET Core Authentication with IdentityServer4
我应该能够通过如下所示的示例请求检索令牌:
POST /connect/token HTTP/1.1
Host: localhost:5000
Cache-Control: no-cache
Postman-Token: 958df72b-663c-5638-052a-aed41ba0dbd1
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=Mike%40Contoso.com&password=MikesPassword1!&client_id=myClient&scope=myAPIs
https://devblogs.microsoft.com/aspnet/asp-net-core-authentication-with-identityserver4/
创建看起来像这样的请求,但针对创建的解决方案:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 153
grant_type=password&username=example%40example.com&password=Password1&client_id=WebApplication4.Client&scope=WebApplication4.ServerAPI%20openid%20profile
此请求 returns HTTP 状态 400 请求正文错误:
{
"error": "unauthorized_client"
}
我很确定这些值是正确的,因为我从用于登录 Web 应用程序的请求中获得了 client_id
和 scope
。但是,该流程不使用 grant_type=password
。来自登录的示例请求:
https://localhost:44388/Identity/Account/Login?ReturnUrl=/connect/authorize/callback?client_id=WebApplication4.Client&redirect_uri=https%3A%2F%2Flocalhost%3A44388%2Fauthentication%2Flogin-callback&response_type=code&scope=WebApplication4.ServerAPI%20openid%20profile&state=12345&code_challenge=12345&code_challenge_method=S256&response_mode=query
确认用户存在并在工作:
我错过了什么?
TLDR:
从 appsettings.json
中删除:
"Clients": {
"WebApplication4.Client": {
"Profile": "IdentityServerSPA"
}
}
编辑Startup.cs
:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
//Use Configuration.GetSection("MySecretValue").Value; to get a value from appsettings.json
ClientSecrets = { new Secret("MySecretValue".Sha256()) },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
此请求有效:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 168
grant_type=password&username=example%40example.com&password=Password1!&client_id=WebApplication4.Integration&scope=WebApplication4.ServerAPI&client_secret=MySecretValue
长答案:
我开始尝试使用日志记录获得更好的错误消息。
我将下面的代码添加到 public static IHostBuilder CreateHostBuilder(string[] args)
中
Program.cs
:
.ConfigureLogging(logging =>
{
logging.ClearProviders();
logging.AddConsole();
})
https://docs.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-5.0
调试时,我可以在发出请求时显示服务器应用程序的输出。它看起来像这样:
info: IdentityServer4.Hosting.IdentityServerMiddleware[0]
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenEndpoint for /connect/token
info: IdentityServer4.Events.DefaultEventService[0]
{
"ClientId": "WebApplication4.Client",
"AuthenticationMethod": "NoSecret",
"Category": "Authentication",
"Name": "Client Authentication Success",
"EventType": "Success",
"Id": 1010,
"ActivityId": "8000000a-0000-8f00-b63f-84710c7967bb",
"TimeStamp": "2021-04-29T11:47:07Z",
"ProcessId": 8436,
"LocalIpAddress": "::1:44388",
"RemoteIpAddress": "::1"
}
fail: IdentityServer4.Validation.TokenRequestValidator[0]
Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = WebApplication4.Client }, details: {
"ClientId": "WebApplication4.Client",
"ClientName": "WebApplication4.Client",
"GrantType": "password",
"Raw": {
"grant_type": "password",
"username": "example@example.com",
"password": "***REDACTED***",
"client_id": "WebApplication4.Client",
"scope": "WebApplication4.ServerAPI"
}
}
info: IdentityServer4.Events.DefaultEventService[0]
{
"ClientId": "WebApplication4.Client",
"ClientName": "WebApplication4.Client",
"Endpoint": "Token",
"GrantType": "password",
"Error": "unauthorized_client",
"Category": "Token",
"Name": "Token Issued Failure",
"EventType": "Failure",
"Id": 2001,
"ActivityId": "8000000a-0000-8f00-b63f-84710c7967bb",
"TimeStamp": "2021-04-29T11:47:07Z",
"ProcessId": 8436,
"LocalIpAddress": "::1:44388",
"RemoteIpAddress": "::1"
}
要查看的错误消息是 Client not authorized for resource owner flow, check the AllowedGrantTypes setting{ client_id = WebApplication4.Client }
。
通过这个错误消息我发现了这个问题:
Question about ASP.NET Core 3 Identity / Identity Server / SPA support for Resource Owner Password Grant Type
在那里我可以阅读
found that the allowed grant type of password was not being added when the profile is set to IdentityServerSPA.
查看 appsettings.json
应用程序使用该配置文件:
"IdentityServer": {
"Clients": {
"WebApplication4.Client": {
"Profile": "IdentityServerSPA"
}
}
},
看看 Microsoft Application profiles
它实际上是这样的:
- redirect_uri默认为/authentication/login-callback。
- post_logout_redirect_uri 默认为 /authentication/logout-callback.
- 范围集包括openid、配置文件和每个范围 为应用程序中的 API 定义。
- 允许的 OIDC 响应类型集是 id_token 令牌或每个 他们分别 (id_token, token).
- 允许的响应模式是片段。
在开始修改之前,我访问了 URL https://localhost:44388/.well-known/openid-configuration
以获取当前配置。它看起来像这样,具体说 grant_types_supported: ...password
:
{
"issuer": "https://localhost:44388",
"jwks_uri": "https://localhost:44388/.well-known/openid-configuration/jwks",
"authorization_endpoint": "https://localhost:44388/connect/authorize",
"token_endpoint": "https://localhost:44388/connect/token",
"userinfo_endpoint": "https://localhost:44388/connect/userinfo",
"end_session_endpoint": "https://localhost:44388/connect/endsession",
"check_session_iframe": "https://localhost:44388/connect/checksession",
"revocation_endpoint": "https://localhost:44388/connect/revocation",
"introspection_endpoint": "https://localhost:44388/connect/introspect",
"device_authorization_endpoint": "https://localhost:44388/connect/deviceauthorization",
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"openid",
"profile",
"WebApplication4.ServerAPI",
"offline_access"
],
"claims_supported": [
"sub",
"name",
"family_name",
"given_name",
"middle_name",
"nickname",
"preferred_username",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true
}
由于某些原因 IdentityServer
Clients
无法在代码和 appsettings.json
中进行配置。因此,我从 appsettings.json
中删除了 Clients
并将其添加到 Startup.cs:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
没有 WithRedirectUri
和 WithLogoutRedirectUri
它不起作用,OidcConfigurationController
得到了 ClientRequestParametersProvider.GetClientParameters(HttpContext, clientId);
和 System.InvalidOperationException: 'Sequence contains no elements'
的异常。出于某种原因,这在使用 appsettings.json
.
我现在在发帖到 /connect/token
时收到错误消息:
{
"error": "invalid_client"
}
但是我在日志中得到了一个更好的错误:
Invalid client configuration for client WebApplication4.Integration: Client secret is required for password, but no client secret is configured.
向 Startup.cs
添加了一个秘密:
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options =>
{
options.Clients.AddIdentityServerSPA("WebApplication4.Client", builder =>
{
builder.WithRedirectUri("/authentication/login-callback");
builder.WithLogoutRedirectUri("/authentication/logout-callback");
});
options.Clients.Add(new IdentityServer4.Models.Client
{
ClientId = "WebApplication4.Integration",
AllowedGrantTypes = { GrantType.ResourceOwnerPassword },
//Use Configuration.GetSection("MySecretValue").Value; to get a value from appsettings.json
ClientSecrets = { new Secret("MySecretValue".Sha256()) },
AllowedScopes = { "WebApplication4.ServerAPI", "openid", "profile" }
});
});
请求:
POST /connect/token HTTP/1.1
Host: localhost:44388
Content-Type: application/x-www-form-urlencoded
Content-Length: 168
grant_type=password&username=example%40example.com&password=Password1!&client_id=WebApplication4.Integration&scope=WebApplication4.ServerAPI&client_secret=MySecretValue
终于成功了,正常的登录流程也成功了!