diff --git a/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj b/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
index c7021e85f0..b522899251 100644
--- a/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
+++ b/src/Volo.Abp.AutoMapper/Volo.Abp.AutoMapper.csproj
@@ -18,7 +18,7 @@
-
+
\ No newline at end of file
diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs
index 384a251721..012282784f 100644
--- a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs
+++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/AbpIdentityServerDomainModule.cs
@@ -19,7 +19,7 @@ namespace Volo.Abp.IdentityServer
{
services.Configure(options =>
{
- options.AddProfile(/*TODO: validate: true*/);
+ options.AddProfile(validate: true);
});
services.AddAssemblyOf();
diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs
new file mode 100644
index 0000000000..759bbbdf02
--- /dev/null
+++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/ApiResources/IApiResourceRepository.cs
@@ -0,0 +1,9 @@
+using Volo.Abp.Domain.Repositories;
+
+namespace Volo.Abp.IdentityServer.ApiResources
+{
+ public interface IApiResourceRepository : IRepository
+ {
+
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientAutoMapperProfile.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientAutoMapperProfile.cs
index fbec0430b8..b76f22e5af 100644
--- a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientAutoMapperProfile.cs
+++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Clients/ClientAutoMapperProfile.cs
@@ -1,4 +1,9 @@
-using AutoMapper;
+using System.Collections.Generic;
+using System.Security.Claims;
+using AutoMapper;
+using Volo.Abp.IdentityServer.ApiResources;
+using Volo.Abp.IdentityServer.Grants;
+using Volo.Abp.IdentityServer.IdentityResources;
namespace Volo.Abp.IdentityServer.Clients
{
@@ -12,6 +17,64 @@ namespace Volo.Abp.IdentityServer.Clients
.ConstructUsing(src => src.Origin)
.ReverseMap()
.ForMember(dest => dest.Origin, opt => opt.MapFrom(src => src));
+
+
+ CreateMap()
+ .ForMember(dest => dest.ApiSecrets, opt => opt.MapFrom(src => src.Secrets));
+
+
+ CreateMap().ReverseMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap();
+
+ CreateMap>()
+ .ReverseMap();
+
+ CreateMap()
+ .ForMember(dest => dest.ProtocolType, opt => opt.Condition(srs => srs != null))
+ .ReverseMap();
+
+ CreateMap()
+ .ConstructUsing(src => src.Origin)
+ .ReverseMap()
+ .ForMember(dest => dest.Origin, opt => opt.MapFrom(src => src));
+
+ CreateMap()
+ .ConstructUsing(src => src.Provider)
+ .ReverseMap()
+ .ForMember(dest => dest.Provider, opt => opt.MapFrom(src => src));
+
+ CreateMap(MemberList.None)
+ .ConstructUsing(src => new Claim(src.Type, src.Value))
+ .ReverseMap();
+
+ CreateMap()
+ .ConstructUsing(src => src.Scope)
+ .ReverseMap()
+ .ForMember(dest => dest.Scope, opt => opt.MapFrom(src => src));
+
+ CreateMap()
+ .ConstructUsing(src => src.PostLogoutRedirectUri)
+ .ReverseMap()
+ .ForMember(dest => dest.PostLogoutRedirectUri, opt => opt.MapFrom(src => src));
+
+ CreateMap()
+ .ConstructUsing(src => src.RedirectUri)
+ .ReverseMap()
+ .ForMember(dest => dest.RedirectUri, opt => opt.MapFrom(src => src));
+
+ CreateMap()
+ .ConstructUsing(src => src.GrantType)
+ .ReverseMap()
+ .ForMember(dest => dest.GrantType, opt => opt.MapFrom(src => src));
+
+ CreateMap(MemberList.Destination)
+ .ForMember(dest => dest.Type, opt => opt.Condition(srs => srs != null))
+ .ReverseMap();
}
}
}
diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs
new file mode 100644
index 0000000000..709fe00d62
--- /dev/null
+++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/IPersistentGrantRepository.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using Volo.Abp.Domain.Repositories;
+
+namespace Volo.Abp.IdentityServer.Grants
+{
+ public interface IPersistentGrantRepository : IRepository
+ {
+ Task FindByKeyAsync(string key);
+
+ Task> GetListBySubjectIdAsync(string key);
+
+ Task DeleteAsync(string subjectId, string clientId);
+
+ Task DeleteAsync(string subjectId, string clientId, string type);
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs
new file mode 100644
index 0000000000..f312132900
--- /dev/null
+++ b/src/Volo.Abp.IdentityServer.Domain/Volo/Abp/IdentityServer/Grants/PersistedGrantStore.cs
@@ -0,0 +1,70 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Dynamic.Core;
+using System.Threading.Tasks;
+using IdentityServer4.Stores;
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Domain.Repositories;
+using Volo.Abp.ObjectMapping;
+
+namespace Volo.Abp.IdentityServer.Grants
+{
+ public class PersistedGrantStore : IPersistedGrantStore, ITransientDependency
+ {
+ private readonly IPersistentGrantRepository _persistentGrantRepository;
+ private readonly IObjectMapper _objectMapper;
+
+ public PersistedGrantStore(IPersistentGrantRepository persistentGrantRepository, IObjectMapper objectMapper)
+ {
+ _persistentGrantRepository = persistentGrantRepository;
+ _objectMapper = objectMapper;
+ }
+
+ public virtual async Task StoreAsync(IdentityServer4.Models.PersistedGrant grant)
+ {
+ var entity = _objectMapper.Map(grant);
+ var existing = await _persistentGrantRepository.FindByKeyAsync(grant.Key);
+ if (existing == null)
+ {
+ await _persistentGrantRepository.InsertAsync(entity);
+ }
+ else
+ {
+ await _persistentGrantRepository.UpdateAsync(entity);
+ }
+ }
+
+ public virtual async Task GetAsync(string key)
+ {
+ var persistedGrant = await _persistentGrantRepository.FindByKeyAsync(key);
+ return _objectMapper.Map(persistedGrant);
+ }
+
+ public virtual async Task> GetAllAsync(string subjectId)
+ {
+ var persistedGrants = await _persistentGrantRepository.GetListBySubjectIdAsync(subjectId);
+ return persistedGrants.Select(x => _objectMapper.Map(x));
+ }
+
+ public virtual async Task RemoveAsync(string key)
+ {
+ var persistedGrant = await _persistentGrantRepository.FindByKeyAsync(key);
+ if (persistedGrant == null)
+ {
+ return;
+ }
+
+ await _persistentGrantRepository.DeleteAsync(persistedGrant);
+ }
+
+ public virtual async Task RemoveAllAsync(string subjectId, string clientId)
+ {
+ await _persistentGrantRepository.DeleteAsync(subjectId, clientId);
+ }
+
+ public virtual async Task RemoveAllAsync(string subjectId, string clientId, string type)
+ {
+ await _persistentGrantRepository.DeleteAsync(subjectId, clientId, type);
+ }
+ }
+}
diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResourceRepository.cs b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResourceRepository.cs
new file mode 100644
index 0000000000..bd62b96b6b
--- /dev/null
+++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/ApiResourceRepository.cs
@@ -0,0 +1,15 @@
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.IdentityServer.ApiResources;
+using Volo.Abp.IdentityServer.EntityFrameworkCore;
+
+namespace Volo.Abp.IdentityServer
+{
+ public class ApiResourceRepository : EfCoreRepository, IApiResourceRepository
+ {
+ public ApiResourceRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/PersistedGrantRepository.cs b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/PersistedGrantRepository.cs
new file mode 100644
index 0000000000..0b1011992c
--- /dev/null
+++ b/src/Volo.Abp.IdentityServer.EntityFrameworkCore/Volo/Abp/IdentityServer/PersistedGrantRepository.cs
@@ -0,0 +1,50 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.EntityFrameworkCore;
+using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.IdentityServer.EntityFrameworkCore;
+using Volo.Abp.IdentityServer.Grants;
+
+namespace Volo.Abp.IdentityServer
+{
+ public class PersistentGrantRepository : EfCoreRepository, IPersistentGrantRepository
+ {
+ public PersistentGrantRepository(IDbContextProvider dbContextProvider) : base(dbContextProvider)
+ {
+
+ }
+
+ public Task FindByKeyAsync(string key)
+ {
+ return DbSet.FirstOrDefaultAsync(x => x.Key == key);
+ }
+
+ public Task> GetListBySubjectIdAsync(string subjectId)
+ {
+ return DbSet.Where(x => x.SubjectId == subjectId).ToListAsync();
+ }
+
+ public Task DeleteAsync(string subjectId, string clientId)
+ {
+ DbSet.RemoveRange(
+ DbSet.Where(x => x.SubjectId == subjectId && x.ClientId == clientId)
+ );
+
+ return Task.FromResult(0);
+ }
+
+ public Task DeleteAsync(string subjectId, string clientId, string type)
+ {
+ DbSet.RemoveRange(
+ DbSet.Where(x =>
+ x.SubjectId == subjectId &&
+ x.ClientId == clientId &&
+ x.Type == type)
+ );
+
+ return Task.FromResult(0);
+ }
+ }
+}
diff --git a/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs
index 6fc4153dc8..b3f1fa420b 100644
--- a/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs
+++ b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/AbpIdentityServerTestDataBuilder.cs
@@ -1,26 +1,36 @@
using System.Collections.Generic;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
+using Volo.Abp.IdentityServer.ApiResources;
using Volo.Abp.IdentityServer.Clients;
+using Volo.Abp.IdentityServer.Grants;
namespace Volo.Abp.IdentityServer
{
public class AbpIdentityServerTestDataBuilder : ITransientDependency
{
- private readonly IClientRepository _clientRepository;
private readonly IGuidGenerator _guidGenerator;
+ private readonly IClientRepository _clientRepository;
+ private readonly IPersistentGrantRepository _persistentGrantRepository;
+ private readonly IApiResourceRepository _apiResourceRepository;
public AbpIdentityServerTestDataBuilder(
- IClientRepository clientRepository,
- IGuidGenerator guidGenerator)
+ IClientRepository clientRepository,
+ IGuidGenerator guidGenerator,
+ IPersistentGrantRepository persistentGrantRepository,
+ IApiResourceRepository apiResourceRepository)
{
_clientRepository = clientRepository;
_guidGenerator = guidGenerator;
+ _persistentGrantRepository = persistentGrantRepository;
+ _apiResourceRepository = apiResourceRepository;
}
public void Build()
{
AddClients();
+ AddPersistentGrants();
+ AddApiResources();
}
private void AddClients()
@@ -37,8 +47,77 @@ namespace Volo.Abp.IdentityServer
Origin = "Origin1"
}
);
-
+
_clientRepository.Insert(client42);
}
+
+ private void AddPersistentGrants()
+ {
+ _persistentGrantRepository.Insert(new PersistedGrant(_guidGenerator.Create())
+ {
+ Key = "38",
+ ClientId = "TestClientId-38",
+ Type = "TestType-38",
+ SubjectId = "TestSubject",
+ Data = "TestData-38"
+ });
+
+ _persistentGrantRepository.Insert(new PersistedGrant(_guidGenerator.Create())
+ {
+ Key = "37",
+ ClientId = "TestClientId-37",
+ Type = "TestType-37",
+ SubjectId = "TestSubject",
+ Data = "TestData-37"
+ });
+
+ _persistentGrantRepository.Insert(new PersistedGrant(_guidGenerator.Create())
+ {
+ Key = "36",
+ ClientId = "TestClientId-X",
+ Type = "TestType-36",
+ SubjectId = "TestSubject-X",
+ Data = "TestData-36"
+ });
+
+ _persistentGrantRepository.Insert(new PersistedGrant(_guidGenerator.Create())
+ {
+ Key = "35",
+ ClientId = "TestClientId-X",
+ Type = "TestType-35",
+ SubjectId = "TestSubject-X",
+ Data = "TestData-35"
+ });
+ }
+
+ private void AddApiResources()
+ {
+ _apiResourceRepository.Insert(new ApiResource(_guidGenerator.Create())
+ {
+ Name = "Test-ApiResource-Name-1",
+ Enabled = true,
+ Description = "Test-ApiResource-Description-1",
+ DisplayName = "Test-ApiResource-DisplayName-1",
+ Secrets = new List
+ {
+ new ApiSecret(_guidGenerator.Create())
+ },
+ UserClaims = new List
+ {
+ new ApiResourceClaim(_guidGenerator.Create())
+ {
+ Type = "Test-ApiResource-Claim-Type-1"
+ }
+ },
+ Scopes = new List
+ {
+ new ApiScope(_guidGenerator.Create())
+ {
+ Name = "Test-ApiResource-ApiScope-Name-1",
+ DisplayName = "Test-ApiResource-ApiScope-DisplayName-1"
+ }
+ }
+ });
+ }
}
}
diff --git a/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/IdentityResourceStore_Tests.cs b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/IdentityResourceStore_Tests.cs
new file mode 100644
index 0000000000..bd15fed89b
--- /dev/null
+++ b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/IdentityResourceStore_Tests.cs
@@ -0,0 +1,92 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using IdentityServer4.Models;
+using IdentityServer4.Stores;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.IdentityServer.Clients
+{
+ public class IdentityResourceStore_Tests : AbpIdentityServerTestBase
+ {
+ private readonly IResourceStore _resourceStore;
+
+ public IdentityResourceStore_Tests()
+ {
+ _resourceStore = ServiceProvider.GetRequiredService();
+ }
+
+ [Fact]
+ public async Task FindApiResourceAsync_Should_Return_Null_If_Not_Found()
+ {
+ //Act
+ var resource = await _resourceStore.FindApiResourceAsync("non-existing-name");
+
+ //Assert
+ resource.ShouldBeNull();
+ }
+
+ [Fact]
+ public async Task FindApiResourceAsync_Should_Return_If_Found()
+ {
+ //Act
+ var apiResource = await _resourceStore.FindApiResourceAsync("Test-ApiResource-Name-1");
+
+ //Assert
+ apiResource.ShouldNotBe(null);
+ apiResource.Name.ShouldBe("Test-ApiResource-Name-1");
+ apiResource.Description.ShouldBe("Test-ApiResource-Description-1");
+ apiResource.DisplayName.ShouldBe("Test-ApiResource-DisplayName-1");
+ }
+
+ [Fact]
+ public async Task FindApiResourcesByScopeAsync_Should_Return_If_Found()
+ {
+ //Act
+ var apiResourcesByScope = await _resourceStore.FindApiResourcesByScopeAsync(new List
+ {
+ "Test-ApiResource-ApiScope-Name-1"
+ });
+
+ //Assert
+ var apiResources = apiResourcesByScope as ApiResource[] ?? apiResourcesByScope.ToArray();
+ apiResources.ShouldNotBe(null);
+
+ apiResources[0].Scopes.GroupBy(x => x.Name).Count().ShouldBe(1);
+ apiResources[0].Scopes.GroupBy(x => x.Name).First().Key.ShouldBe("Test-ApiResource-ApiScope-Name-1");
+ }
+
+ [Fact]
+ public async Task FindIdentityResourcesByScopeAsync_Should_Return_For_Given_Scopes()
+ {
+ //Act
+ var identityResourcesByScope = await _resourceStore.FindIdentityResourcesByScopeAsync(new List
+ {
+ "Test-Identity-Resource-Name-1"
+ });
+
+ //Assert
+ var resourcesByScope = identityResourcesByScope as IdentityResource[] ?? identityResourcesByScope.ToArray();
+ resourcesByScope.Length.ShouldBe(1);
+ resourcesByScope.First().DisplayName.ShouldBe("Test-Identity-Resource-DisplayName-1");
+ resourcesByScope.First().Description.ShouldBe("Test-Identity-Resource-Description-1");
+ resourcesByScope.First().Required.ShouldBe(true);
+ }
+
+ [Fact]
+ public async Task GetAllResourcesAsync_Should_Return()
+ {
+ //Act
+ var resources = await _resourceStore.GetAllResourcesAsync();
+
+ //Assert
+ resources.ShouldNotBe(null);
+ resources.ApiResources.Count.ShouldBe(1);
+ resources.ApiResources.First().Name.ShouldBe("Test-ApiResource-Name-1");
+ resources.IdentityResources.First().Name.ShouldBe("Test-Identity-Resource-Name-1");
+ resources.IdentityResources.First().Required.ShouldBe(true);
+ }
+ }
+}
diff --git a/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs
new file mode 100644
index 0000000000..9a87e30d64
--- /dev/null
+++ b/test/Abp.IdentityServer.EntityFrameworkCore.Tests/Volo/Abp/IdentityServer/Clients/PersistentGrant_Tests.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Linq;
+using System.Threading.Tasks;
+using IdentityServer4.Models;
+using IdentityServer4.Stores;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using Xunit;
+
+namespace Volo.Abp.IdentityServer.Clients
+{
+ public class PersistentGrantStore_Tests : AbpIdentityServerTestBase
+ {
+ private readonly IPersistedGrantStore _persistedGrantStore;
+
+ public PersistentGrantStore_Tests()
+ {
+ _persistedGrantStore = ServiceProvider.GetRequiredService();
+ }
+
+ [Fact]
+ public async Task FindClientByIdAsync_Should_Return_Null_If_Not_Found()
+ {
+ var persistentGrant = await _persistedGrantStore.GetAsync("not-existing-id");
+ persistentGrant.ShouldBeNull();
+ }
+
+ [Fact]
+ public async Task FindPersistentGrantByIdAsync_Should_Return_The_PersistentGrant_If_Found()
+ {
+ //Act
+ var client = await _persistedGrantStore.GetAsync("38");
+
+ //Assert
+ client.ShouldNotBeNull();
+ client.ClientId.ShouldBe("TestClientId-38");
+ client.SubjectId.ShouldBe("TestSubject");
+ client.Data.ShouldContain("TestData-38");
+ client.Type.ShouldContain("TestType-38");
+ }
+
+ [Fact]
+ public async Task StoreAsync_Should_Store_PersistedGrant()
+ {
+ //Act
+ await _persistedGrantStore.StoreAsync(new PersistedGrant
+ {
+ Key = "39",
+ ClientId = "TestClientId-39",
+ Type = "TestType-39",
+ SubjectId = "TestSubject",
+ Data = "TestData-39",
+ Expiration = new DateTime(2018, 1, 6, 21, 22, 23),
+ CreationTime = new DateTime(2018, 1, 5, 19, 20, 21)
+ });
+
+ //Assert
+ var persistedGrant = await _persistedGrantStore.GetAsync("39");
+ persistedGrant.Key.ShouldBe("39");
+ persistedGrant.ClientId.ShouldBe("TestClientId-39");
+ persistedGrant.Type.ShouldBe("TestType-39");
+ persistedGrant.SubjectId.ShouldBe("TestSubject");
+ persistedGrant.Data.ShouldBe("TestData-39");
+
+ persistedGrant.Expiration.HasValue.ShouldBe(true);
+ persistedGrant.Expiration.Value.Year.ShouldBe(2018);
+ persistedGrant.Expiration.Value.Month.ShouldBe(1);
+ persistedGrant.Expiration.Value.Day.ShouldBe(6);
+ persistedGrant.Expiration.Value.Hour.ShouldBe(21);
+ persistedGrant.Expiration.Value.Minute.ShouldBe(22);
+ persistedGrant.Expiration.Value.Second.ShouldBe(23);
+
+ persistedGrant.CreationTime.Year.ShouldBe(2018);
+ persistedGrant.CreationTime.Month.ShouldBe(1);
+ persistedGrant.CreationTime.Day.ShouldBe(5);
+ persistedGrant.CreationTime.Hour.ShouldBe(19);
+ persistedGrant.CreationTime.Minute.ShouldBe(20);
+ persistedGrant.CreationTime.Second.ShouldBe(21);
+ }
+
+ [Fact]
+ public async Task GetAllAsync_Should_Get_All_PersistedGrants_For_A_Given_SubjectId()
+ {
+ //Act
+ var persistentGrants = await _persistedGrantStore.GetAllAsync("TestSubject");
+
+ //Assert
+ var persistedGrants = persistentGrants as PersistedGrant[] ?? persistentGrants.ToArray();
+ persistedGrants.ShouldNotBe(null);
+ persistedGrants.Length.ShouldBe(2);
+ persistedGrants[0].SubjectId.ShouldBe("TestSubject");
+ persistedGrants[1].SubjectId.ShouldBe("TestSubject");
+ }
+
+ [Fact]
+ public async Task RemoveAsync_Should_Remove_PeristedGrant()
+ {
+ //Arrange
+ await _persistedGrantStore.StoreAsync(new PersistedGrant
+ {
+ Key = "#1P3R"
+ });
+
+ //Act
+ await _persistedGrantStore.RemoveAsync("#1P3R");
+
+ //Assert
+ var persistedGrant = await _persistedGrantStore.GetAsync("#1P3R");
+ persistedGrant.ShouldBe(null);
+ }
+
+ [Fact]
+ public async Task RemoveAllAsync_Should_RemoveAll_PeristedGrants_For_A_Given_Subject_And_ClientId()
+ {
+ //Arrange
+ var persistedGrantsWithTestSubjectX = await _persistedGrantStore.GetAllAsync("TestSubject-X");
+ var persistedGrantsWithTestSubjectXBeforeLength = persistedGrantsWithTestSubjectX.ToArray().Length;
+
+ //Act
+ await _persistedGrantStore.RemoveAllAsync("TestSubject-X", "TestClientId-X");
+
+ //Assert
+ persistedGrantsWithTestSubjectXBeforeLength.ShouldBe(2);
+
+ var persistedGrants = (await _persistedGrantStore.GetAllAsync("TestClientId-37")).ToArray();
+ persistedGrants.ShouldNotBe(null);
+ persistedGrants.Length.ShouldBe(0);
+ }
+ }
+}