Я реализовал пользовательский PersistedGrantStore, хранящий мои токены обновления в xml-файле, однако теперь у меня возникают проблемы с обновлением моих токенов.
Когда я удаляю следующие строки, обновление работает
services.AddTransient<IPersistedGrantService, PersistedGrantService>();
services.AddTransient<IPersistedGrantStore, PersistedGrantStore>();
Когда я помещаю эти строки обратно, я получаю в логах следующее:
2018-04-24 14:14:51.094 +02:00 [DBG] [IdentityServer4.Hosting.EndpointRouter] Endpoint enabled: Token, successfully created handler: IdentityServer4.Endpoints.TokenEndpoint
2018-04-24 14:14:51.095 +02:00 [INF] [IdentityServer4.Hosting.IdentityServerMiddleware] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.TokenEndpoint for /connect/token
2018-04-24 14:14:51.098 +02:00 [VRB] [IdentityServer4.Endpoints.TokenEndpoint] Processing token request.
2018-04-24 14:14:51.108 +02:00 [DBG] [IdentityServer4.Endpoints.TokenEndpoint] Start token request.
2018-04-24 14:14:51.115 +02:00 [DBG] [IdentityServer4.Validation.ClientSecretValidator] Start client validation
2018-04-24 14:14:51.120 +02:00 [DBG] [IdentityServer4.Validation.BasicAuthenticationSecretParser] Start parsing Basic Authentication secret
2018-04-24 14:14:51.123 +02:00 [DBG] [IdentityServer4.Validation.PostBodySecretParser] Start parsing for secret in post body
2018-04-24 14:14:51.145 +02:00 [DBG] [IdentityServer4.Validation.SecretParser] Parser found secret: PostBodySecretParser
2018-04-24 14:14:51.145 +02:00 [DBG] [IdentityServer4.Validation.SecretParser] Secret id found: UserPortal
2018-04-24 14:14:51.161 +02:00 [DBG] [IdentityServer4.Validation.SecretValidator] Secret validator success: HashedSharedSecretValidator
2018-04-24 14:14:51.161 +02:00 [DBG] [IdentityServer4.Validation.ClientSecretValidator] Client validation success
2018-04-24 14:14:51.167 +02:00 [VRB] [IdentityServer4.Endpoints.TokenEndpoint] Calling into token request validator: IdentityServer4.Validation.TokenRequestValidator
2018-04-24 14:14:51.173 +02:00 [DBG] [IdentityServer4.Validation.TokenRequestValidator] Start token request validation
2018-04-24 14:14:51.181 +02:00 [DBG] [IdentityServer4.Validation.TokenRequestValidator] Start validation of refresh token request
2018-04-24 14:14:51.186 +02:00 [VRB] [IdentityServer4.Validation.TokenValidator] Start refresh token validation
2018-04-24 14:14:51.228 +02:00 [DBG] [Nextel.VisitorManager.IdentityServer.Extensions.PersistedGrantStore] r9bsqJEQddM1Hhp6PzZwk/Zr3Yyb72PntfPZup+ik5Y= found in database: True
2018-04-24 14:14:51.992 +02:00 [ERR] [IdentityServer4.Validation.TokenValidator] Refresh token has expired. Removing from store.
2018-04-24 14:14:51.996 +02:00 [DBG] [Nextel.VisitorManager.IdentityServer.Extensions.PersistedGrantStore] removing r9bsqJEQddM1Hhp6PzZwk/Zr3Yyb72PntfPZup+ik5Y= persisted grant from database
2018-04-24 14:14:52.002 +02:00 [ERR] [IdentityServer4.Validation.TokenRequestValidator] Refresh token validation failed. aborting.
2018-04-24 14:14:52.095 +02:00 [ERR] [IdentityServer4.Validation.TokenRequestValidator] {
"ClientId": "UserPortal",
"ClientName": "User Portal Client",
"GrantType": "refresh_token",
"Raw": {
"client_id": "UserPortal",
"client_secret": "***REDACTED***",
"grant_type": "refresh_token",
"refresh_token": "3314d145ec7dec80b8137b288c99a66b22798451dd77a8878466139da29a3c13",
"scope": "offline_access"
}
}
2018-04-24 14:14:52.098 +02:00 [VRB] [IdentityServer4.Hosting.IdentityServerMiddleware] Invoking result: IdentityServer4.Endpoints.Results.TokenErrorResult
Что, похоже, показывает, что он действительно находит токен обновления в хранилище, но затем удаляет его и возвращает недействительный_грамм
Это мой PersistedGrantStore:
public class PersistedGrantStore : IPersistedGrantStore
{
#region Declarations
private readonly ILogger logger;
private readonly IPersistedGrantService persistedGrantService;
#endregion
#region Constructor
public PersistedGrantStore(IPersistedGrantService persistedGrantService, ILogger<PersistedGrantStore> logger)
{
this.persistedGrantService = persistedGrantService;
this.logger = logger;
}
#endregion
#region Methods
public Task StoreAsync(PersistedGrant token)
{
var existing = persistedGrantService.GetByKey(token.Key);
if (existing == null)
{
logger.LogDebug($"{token.Key} not found in database");
persistedGrantService.Add(token);
}
else
{
logger.LogDebug($"{token.Key} found in database");
persistedGrantService.Update(token);
}
return Task.FromResult(0);
}
public Task<PersistedGrant> GetAsync(string key)
{
var persistedGrant = persistedGrantService.GetByKey(key);
logger.LogDebug($"{key} found in database: {persistedGrant != null}");
return Task.FromResult(persistedGrant);
}
public Task<IEnumerable<PersistedGrant>> GetAllAsync(string subjectId)
{
var persistedGrants = persistedGrantService.GetBySubject(subjectId);
logger.LogDebug($"{persistedGrants.Count} persisted grants found for {subjectId}");
return Task.FromResult(persistedGrants.AsEnumerable());
}
public Task RemoveAsync(string key)
{
var persistedGrant = persistedGrantService.GetByKey(key);
if (persistedGrant != null)
{
logger.LogDebug($"removing {key} persisted grant from database");
persistedGrantService.Remove(persistedGrant);
}
else
logger.LogDebug($"no {key} persisted grant found in database");
return Task.FromResult(0);
}
public Task RemoveAllAsync(string subjectId, string clientId)
{
var persistedGrants = persistedGrantService.GetBySubjectClient(subjectId, clientId);
logger.LogDebug($"removing {persistedGrants.Count} persisted grants from database for subject {subjectId}, clientId {clientId}");
persistedGrantService.Remove(persistedGrants);
return Task.FromResult(0);
}
public Task RemoveAllAsync(string subjectId, string clientId, string type)
{
var persistedGrants = persistedGrantService.GetBySubjectClientType(subjectId, clientId, type);
logger.LogDebug($"removing {persistedGrants.Count} persisted grants from database for subject {subjectId}, clientId {clientId}, grantType {type}");
persistedGrantService.Remove(persistedGrants);
return Task.FromResult(0);
}
#endregion
}
А это мой PersistedGrantService:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
namespace Nextel.VisitorManager.IdentityServer.Extensions
{
public class PersistedGrantService : IPersistedGrantService
{
#region Declarations
private readonly ILogger logger;
private List<PersistedGrant> persistedGrants;
private readonly string xmlFile;
#endregion
#region Constructor
public PersistedGrantService(IHostingEnvironment env, ILogger<PersistedGrantStore> logger)
{
this.logger = logger;
xmlFile = env.ContentRootPath + @"\PersistedGrants.xml";
}
#endregion
#region Methods
public PersistedGrant GetByKey(string key)
{
if (persistedGrants != null || ReadXml())
return persistedGrants.FirstOrDefault(x => x.Key == key);
return null;
}
public List<PersistedGrant> GetBySubject(string subjectId)
{
if (persistedGrants != null || ReadXml())
return persistedGrants.Where(x => x.SubjectId == subjectId).ToList();
return null;
}
public List<PersistedGrant> GetBySubjectClient(string subjectId, string clientId)
{
if (persistedGrants != null || ReadXml())
return persistedGrants.Where(x => x.SubjectId == subjectId && x.ClientId == clientId).ToList();
return null;
}
public List<PersistedGrant> GetBySubjectClientType(string subjectId, string clientId, string type)
{
if (persistedGrants != null || ReadXml())
return persistedGrants.Where(x => x.SubjectId == subjectId && x.ClientId == clientId && x.Type == type).ToList();
return null;
}
public void Add(PersistedGrant item)
{
if (persistedGrants != null || ReadXml())
{
persistedGrants.Add(item);
SaveXml();
}
}
public void Update(PersistedGrant item)
{
if (persistedGrants != null || ReadXml())
{
var index = persistedGrants.FindIndex(x => x.Key == item.Key);
persistedGrants[index] = item;
SaveXml();
}
}
public void Remove(PersistedGrant item)
{
if (persistedGrants != null || ReadXml())
{
persistedGrants.Remove(item);
SaveXml();
}
}
public void Remove(List<PersistedGrant> items)
{
if (persistedGrants != null || ReadXml())
{
foreach (var item in items)
{
persistedGrants.Remove(item);
}
SaveXml();
}
}
#region Privates
private bool ReadXml()
{
try
{
if (File.Exists(xmlFile))
{
var deserializer = new XmlSerializer(typeof(List<PersistedGrant>));
using (TextReader reader = new StreamReader(xmlFile))
{
persistedGrants = (List<PersistedGrant>)deserializer.Deserialize(reader);
}
}
else
persistedGrants = new List<PersistedGrant>();
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Error in reading XML");
return false;
}
}
private bool SaveXml()
{
try
{
if (persistedGrants != null)
{
var serializer = new XmlSerializer(typeof(List<PersistedGrant>));
using (TextWriter writer = new StreamWriter(xmlFile))
{
serializer.Serialize(writer, persistedGrants);
}
}
return true;
}
catch (Exception ex)
{
logger.LogError(ex, "Error in saving XML");
return false;
}
}
#endregion
#endregion
}
}
С этим интерфейсом:
public interface IPersistedGrantService
{
PersistedGrant GetByKey(string key);
List<PersistedGrant> GetBySubject(string subjectId);
List<PersistedGrant> GetBySubjectClient(string subjectId, string clientId);
List<PersistedGrant> GetBySubjectClientType(string subjectId, string clientId, string type);
void Add(PersistedGrant item);
void Update(PersistedGrant item);
void Remove(PersistedGrant item);
void Remove(List<PersistedGrant> items);
}