IdentityServer4 System.ArgumentNullException: 值不能为空。参数名称:尝试刷新令牌时键入
IdentityServer4 System.ArgumentNullException: Value cannot be null. Parameter name: type when trying to refresh token
我将 identityserver4 v2.4 设置为用户 ASP.Net 身份和 Entity Framework 作为运营商店。当我如下调用 tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
时,我总是在 tokenResult
中得到 invalid_grant
错误
TokenClient tokenClient = new TokenClient(tokenEndpoint, clientId, clientSecret);
string oldRefreshToken = await context.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
TokenResponse tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
IDS4 日志在日志中显示一个 ArgumentException(请注意,参数名称是 value
而不是我的 Parameter name: type
有很多类似的问题。我没有认为它们是相关的,因为为它们推荐的修复对我不起作用)。 IDS4 ClaimsConverter 似乎引发了异常。我确实检查了我的 MySQL 数据库中 asp.net 用户和声明 table 中的任何空值字段,并确认有 none:
[14:17:12 Debug] IdentityServer4.Validation.TokenRequestValidator
Start token request validation
[14:17:12 Debug] IdentityServer4.Validation.TokenRequestValidator
Start validation of refresh token request
[14:17:12 Debug] IdentityServer4.EntityFramework.Stores.PersistedGrantStore
pY3Q91B7RFXV2ilzuJtI+ggqkOg9xiRx4HcGZMfJf+0= found in database: True
[14:17:12 Error] IdentityServer4.Stores.DefaultRefreshTokenStore
Failed to deserialize JSON from grant store.
System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value, String valueType)
at IdentityServer4.Stores.Serialization.ClaimConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in C:\local\identity\server4\IdentityServer4\src\Storage\src\Stores\Serialization\ClaimConverter.cs:line 24
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at IdentityServer4.Stores.DefaultGrantStore`1.GetItemAsync(String key) in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\src\Stores\Default\DefaultGrantStore.cs:line 92
[14:17:12 Warning] IdentityServer4.Validation.TokenValidator
Invalid refresh token
[14:17:12 Warning] IdentityServer4.Validation.TokenRequestValidator
Refresh token validation failed. aborting.
我的IDS4客户端配置如下
new Client
{
ClientId = "AspNetClient",
ClientName = "ASP.Net Client",
RedirectUris = { "http://localhost:5003/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },
AllowedGrantTypes = GrantTypes.Hybrid,
RequireClientSecret = true,
ClientSecrets =
{
new Secret("AspNetClientSecret".Sha256())
},
RequireConsent = false,
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"AccessTokenAuthorizedApi"
},
AllowOfflineAccess = true,
AccessTokenLifetime = (int) TimeSpan.FromSeconds(10).TotalSeconds,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime = (int) TimeSpan.FromDays(30).TotalSeconds,
UpdateAccessTokenClaimsOnRefresh = true
}
我做错了什么?
更新 2019-05-15:
所以我做了更多的挖掘,并使用在日志中找到的密钥 pY3Q91B7RFXV2ilzuJtI+ggqkOg9xiRx4HcGZMfJf+0=
检索了 PersistedGrants table 中的实际记录。我可以在 Data
字段的 JSON 文本中看到 4 个空声明,这一定是导致问题的原因。但为什么 IDS4 会添加这样的空声明?
{
"CreationTime":"2019-05-15T08:47:03Z",
"Lifetime":2592000,
"AccessToken":{
"Audiences":[
"http://localhost:5000/resources",
"AccessTokenAuthorizedApi"
],
"Issuer":"http://localhost:5000",
"CreationTime":"2019-05-15T08:47:03Z",
"Lifetime":10,
"Type":"access_token",
"ClientId":"AspNetClient",
"AccessTokenType":0,
"Claims":[
{
"Type":"client_id",
"Value":"AspNetClient",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"openid",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"profile",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"email",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"AccessTokenAuthorizedApi",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"offline_access",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"sub",
"Value":"0170490c-24c9-4be4-ae2a-5d4b1c55346e",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"auth_time",
"Value":"1557910023",
"ValueType":"http://www.w3.org/2001/XMLSchema#integer"
},
{
"Type":"idp",
"Value":"local",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"amr",
"Value":"pwd",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"AspNet.Identity.SecurityStamp",
"Value":"IF6RRMICBOOITA56FEJLPTFBXCIUKW3Z",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"role",
"Value":"Driver",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"role",
"Value":"Helper",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"preferred_username",
"Value":"hybrid1",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"name",
"Value":"hybrid1",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
},
{
},
{
},
{
}
],
"Version":4
},
"Version":4
}
好的,看起来 IDS4 没有像我预期的那样序列化派生声明对象。我使用的是自定义 TenantClaim class,它继承了 Claim 并在 IdentityServer 项目的自定义 IProfileService 实现中添加了 4 个这样的对象,如下所示:
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Other Code
var tenantRoleClaim = new TenantRoleClaim(tenantRole.TenantId, tenantRole.Role);
claims.Add(tenantRoleClaim);
context.IssuedClaims = claims;
}
一旦我将它更改为使用普通的旧 Claim 类型对象,代码就可以工作了。
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Other Code
var tenantRoleClaim = new TenantRoleClaim(tenantRole.TenantId, tenantRole.Role);
claims.Add(new Claim(tenantRoleClaim.Type, tenantRoleClaim.Value));
context.IssuedClaims = claims;
}
我将 identityserver4 v2.4 设置为用户 ASP.Net 身份和 Entity Framework 作为运营商店。当我如下调用 tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
时,我总是在 tokenResult
invalid_grant
错误
TokenClient tokenClient = new TokenClient(tokenEndpoint, clientId, clientSecret);
string oldRefreshToken = await context.GetTokenAsync(OpenIdConnectParameterNames.RefreshToken);
TokenResponse tokenResult = await tokenClient.RequestRefreshTokenAsync(oldRefreshToken);
IDS4 日志在日志中显示一个 ArgumentException(请注意,参数名称是 value
而不是我的 Parameter name: type
有很多类似的问题。我没有认为它们是相关的,因为为它们推荐的修复对我不起作用)。 IDS4 ClaimsConverter 似乎引发了异常。我确实检查了我的 MySQL 数据库中 asp.net 用户和声明 table 中的任何空值字段,并确认有 none:
[14:17:12 Debug] IdentityServer4.Validation.TokenRequestValidator
Start token request validation
[14:17:12 Debug] IdentityServer4.Validation.TokenRequestValidator
Start validation of refresh token request
[14:17:12 Debug] IdentityServer4.EntityFramework.Stores.PersistedGrantStore
pY3Q91B7RFXV2ilzuJtI+ggqkOg9xiRx4HcGZMfJf+0= found in database: True
[14:17:12 Error] IdentityServer4.Stores.DefaultRefreshTokenStore
Failed to deserialize JSON from grant store.
System.ArgumentNullException: Value cannot be null.
Parameter name: type
at System.Security.Claims.Claim..ctor(String type, String value, String valueType, String issuer, String originalIssuer, ClaimsIdentity subject, String propertyKey, String propertyValue)
at System.Security.Claims.Claim..ctor(String type, String value, String valueType)
at IdentityServer4.Stores.Serialization.ClaimConverter.ReadJson(JsonReader reader, Type objectType, Object existingValue, JsonSerializer serializer) in C:\local\identity\server4\IdentityServer4\src\Storage\src\Stores\Serialization\ClaimConverter.cs:line 24
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.DeserializeConvertable(JsonConverter converter, JsonReader reader, Type objectType, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateList(IList list, JsonReader reader, JsonArrayContract contract, JsonProperty containerProperty, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, Object existingValue, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.SetPropertyValue(JsonProperty property, JsonConverter propertyConverter, JsonContainerContract containerContract, JsonProperty containerProperty, JsonReader reader, Object target)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.PopulateObject(Object newObject, JsonReader reader, JsonObjectContract contract, JsonProperty member, String id)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, JsonContainerContract containerContract, JsonProperty containerMember, Object existingValue)
at Newtonsoft.Json.Serialization.JsonSerializerInternalReader.Deserialize(JsonReader reader, Type objectType, Boolean checkAdditionalContent)
at Newtonsoft.Json.JsonSerializer.DeserializeInternal(JsonReader reader, Type objectType)
at Newtonsoft.Json.JsonConvert.DeserializeObject(String value, Type type, JsonSerializerSettings settings)
at Newtonsoft.Json.JsonConvert.DeserializeObject[T](String value, JsonSerializerSettings settings)
at IdentityServer4.Stores.DefaultGrantStore`1.GetItemAsync(String key) in C:\local\identity\server4\IdentityServer4\src\IdentityServer4\src\Stores\Default\DefaultGrantStore.cs:line 92
[14:17:12 Warning] IdentityServer4.Validation.TokenValidator
Invalid refresh token
[14:17:12 Warning] IdentityServer4.Validation.TokenRequestValidator
Refresh token validation failed. aborting.
我的IDS4客户端配置如下
new Client
{
ClientId = "AspNetClient",
ClientName = "ASP.Net Client",
RedirectUris = { "http://localhost:5003/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5003/signout-callback-oidc" },
AllowedGrantTypes = GrantTypes.Hybrid,
RequireClientSecret = true,
ClientSecrets =
{
new Secret("AspNetClientSecret".Sha256())
},
RequireConsent = false,
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"AccessTokenAuthorizedApi"
},
AllowOfflineAccess = true,
AccessTokenLifetime = (int) TimeSpan.FromSeconds(10).TotalSeconds,
RefreshTokenUsage = TokenUsage.ReUse,
RefreshTokenExpiration = TokenExpiration.Absolute,
AbsoluteRefreshTokenLifetime = (int) TimeSpan.FromDays(30).TotalSeconds,
UpdateAccessTokenClaimsOnRefresh = true
}
我做错了什么?
更新 2019-05-15:
所以我做了更多的挖掘,并使用在日志中找到的密钥 pY3Q91B7RFXV2ilzuJtI+ggqkOg9xiRx4HcGZMfJf+0=
检索了 PersistedGrants table 中的实际记录。我可以在 Data
字段的 JSON 文本中看到 4 个空声明,这一定是导致问题的原因。但为什么 IDS4 会添加这样的空声明?
{
"CreationTime":"2019-05-15T08:47:03Z",
"Lifetime":2592000,
"AccessToken":{
"Audiences":[
"http://localhost:5000/resources",
"AccessTokenAuthorizedApi"
],
"Issuer":"http://localhost:5000",
"CreationTime":"2019-05-15T08:47:03Z",
"Lifetime":10,
"Type":"access_token",
"ClientId":"AspNetClient",
"AccessTokenType":0,
"Claims":[
{
"Type":"client_id",
"Value":"AspNetClient",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"openid",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"profile",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"email",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"AccessTokenAuthorizedApi",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"scope",
"Value":"offline_access",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"sub",
"Value":"0170490c-24c9-4be4-ae2a-5d4b1c55346e",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"auth_time",
"Value":"1557910023",
"ValueType":"http://www.w3.org/2001/XMLSchema#integer"
},
{
"Type":"idp",
"Value":"local",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"amr",
"Value":"pwd",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"AspNet.Identity.SecurityStamp",
"Value":"IF6RRMICBOOITA56FEJLPTFBXCIUKW3Z",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"role",
"Value":"Driver",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"role",
"Value":"Helper",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"preferred_username",
"Value":"hybrid1",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
"Type":"name",
"Value":"hybrid1",
"ValueType":"http://www.w3.org/2001/XMLSchema#string"
},
{
},
{
},
{
},
{
}
],
"Version":4
},
"Version":4
}
好的,看起来 IDS4 没有像我预期的那样序列化派生声明对象。我使用的是自定义 TenantClaim class,它继承了 Claim 并在 IdentityServer 项目的自定义 IProfileService 实现中添加了 4 个这样的对象,如下所示:
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Other Code
var tenantRoleClaim = new TenantRoleClaim(tenantRole.TenantId, tenantRole.Role);
claims.Add(tenantRoleClaim);
context.IssuedClaims = claims;
}
一旦我将它更改为使用普通的旧 Claim 类型对象,代码就可以工作了。
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
// Other Code
var tenantRoleClaim = new TenantRoleClaim(tenantRole.TenantId, tenantRole.Role);
claims.Add(new Claim(tenantRoleClaim.Type, tenantRoleClaim.Value));
context.IssuedClaims = claims;
}