Проблемы с токенами обновления в реализации IdentyServer4 и IPersistedGrantStore - PullRequest
0 голосов
/ 24 апреля 2018

Я реализовал пользовательский 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);


    }

1 Ответ

0 голосов
/ 19 сентября 2018

У меня был почти похожий код для обновления токена, и я часами проводил вместе, чтобы решить ту же проблему.Обнаружено, что в конфигурации клиента должен быть установлен атрибут «RefreshTokenExpiration» в качестве TokenExpiration.Absolute, а в атрибуте «RefreshTokenUsage» установлено значение «OneTimeOnly».Это должно заставить его работать!

...