diff --git a/test/Duende.Bff.Blazor.UnitTests/Duende.Bff.Blazor.UnitTests.csproj b/test/Duende.Bff.Blazor.UnitTests/Duende.Bff.Blazor.UnitTests.csproj
index 9c5b30a2..2c104e3d 100644
--- a/test/Duende.Bff.Blazor.UnitTests/Duende.Bff.Blazor.UnitTests.csproj
+++ b/test/Duende.Bff.Blazor.UnitTests/Duende.Bff.Blazor.UnitTests.csproj
@@ -12,6 +12,8 @@
+
+
@@ -20,4 +22,8 @@
+
+
+
+
diff --git a/test/Duende.Bff.Blazor.UnitTests/ServerSideTokenStoreTests.cs b/test/Duende.Bff.Blazor.UnitTests/ServerSideTokenStoreTests.cs
new file mode 100644
index 00000000..ec82a694
--- /dev/null
+++ b/test/Duende.Bff.Blazor.UnitTests/ServerSideTokenStoreTests.cs
@@ -0,0 +1,84 @@
+using System.Security.Claims;
+using Duende.AccessTokenManagement.OpenIdConnect;
+using Microsoft.AspNetCore.Authentication;
+using Microsoft.AspNetCore.Authentication.Cookies;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using NSubstitute;
+using Shouldly;
+
+namespace Duende.Bff.Blazor.UnitTests;
+
+public class ServerSideTokenStoreTests
+{
+ private ClaimsPrincipal CreatePrincipal(string sub, string sid)
+ {
+ return new ClaimsPrincipal(new ClaimsIdentity([
+ new Claim("sub", sub),
+ new Claim("sid", sid)
+ ], "pwd", "name", "role"));
+ }
+
+ [Fact]
+ public async Task Can_add_retrieve_and_remove_tokens()
+ {
+ var user = CreatePrincipal("sub", "sid");
+ var props = new AuthenticationProperties();
+ var expectedToken = new UserToken()
+ {
+ AccessToken = "expected-access-token"
+ };
+
+ // Create shared dependencies
+ var sessionStore = new InMemoryUserSessionStore();
+ var dataProtection = new EphemeralDataProtectionProvider();
+
+ // Use the ticket store to save the user's initial session
+ // Note that we don't yet have tokens in the session
+ var sessionService = new ServerSideTicketStore(sessionStore, dataProtection, Substitute.For>());
+ sessionService.StoreAsync(new AuthenticationTicket(
+ user,
+ props,
+ "test"
+ ));
+
+ var tokensInProps = MockStoreTokensInAuthProps();
+ var sut = new ServerSideTokenStore(
+ tokensInProps,
+ sessionStore,
+ dataProtection,
+ Substitute.For>());
+
+ await sut.StoreTokenAsync(user, expectedToken);
+ var actualToken = await sut.GetTokenAsync(user);
+
+ actualToken.ShouldNotBe(null);
+ actualToken.AccessToken.ShouldBe(expectedToken.AccessToken);
+
+ await sut.ClearTokenAsync(user);
+
+ var resultAfterClearing = await sut.GetTokenAsync(user);
+ resultAfterClearing.AccessToken.ShouldBeNull();
+ }
+
+ private static StoreTokensInAuthenticationProperties MockStoreTokensInAuthProps()
+ {
+ var tokenManagementOptionsMonitor = Substitute.For>();
+ var tokenManagementOptions = new UserTokenManagementOptions { UseChallengeSchemeScopedTokens = false };
+ tokenManagementOptionsMonitor.CurrentValue.Returns(tokenManagementOptions);
+
+ var cookieOptionsMonitor = Substitute.For>();
+ var cookieAuthenticationOptions = new CookieAuthenticationOptions();
+ cookieOptionsMonitor.CurrentValue.Returns(cookieAuthenticationOptions);
+
+ var schemeProvider = Substitute.For();
+ schemeProvider.GetDefaultSignInSchemeAsync().Returns(new AuthenticationScheme("TestScheme", null, typeof(IAuthenticationHandler)));
+
+ return new StoreTokensInAuthenticationProperties(
+ tokenManagementOptionsMonitor,
+ cookieOptionsMonitor,
+ schemeProvider,
+ Substitute.For>());
+ }
+}
\ No newline at end of file