Skip to content

Commit 0e59cb7

Browse files
committed
add cookie storage
1 parent f612b4e commit 0e59cb7

6 files changed

Lines changed: 86 additions & 42 deletions

File tree

src/ActiveLogin.Authentication.BankId.AspNetCore/Auth/AuthenticationBuilderBankIdExtensions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ private static void AddBankIdAuthDefaultServices(IBankIdAuthBuilder builder)
7373

7474
BankIdCommonConfiguration.AddDefaultServices(services);
7575

76-
services.AddSingleton<IStateStorage, InMemoryStateStorage>();
76+
// services.AddSingleton<IStateStorage, InMemoryStateStorage>();
77+
services.AddSingleton<IStateStorage, CookieStateStorage>();
7778
services.AddTransient<IBankIdUiResultProtector, BankIdUiResultProtector>();
7879

7980
builder.AddClaimsTransformer<BankIdDefaultClaimsTransformer>();
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using ActiveLogin.Authentication.BankId.Core;
2+
3+
using Microsoft.AspNetCore.Http;
4+
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Text.Json;
7+
using System.Text.Json.Serialization;
8+
9+
namespace ActiveLogin.Authentication.BankId.AspNetCore;
10+
11+
public class CookieStateStorage(
12+
IHttpContextAccessor httpContextAccessor
13+
) : IStateStorage
14+
{
15+
private readonly IHttpContextAccessor _httpContextAccessor = httpContextAccessor;
16+
private readonly JsonSerializerOptions _jsonSerializerOptions = new()
17+
{
18+
PropertyNameCaseInsensitive = true,
19+
Converters =
20+
{
21+
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
22+
}
23+
};
24+
25+
private readonly CookieOptions _cookieOptions = new()
26+
{
27+
HttpOnly = true,
28+
Secure = true,
29+
SameSite = SameSiteMode.Strict,
30+
Path = "/",
31+
Expires = DateTimeOffset.UtcNow.AddMinutes(5) // Set expiration as needed
32+
};
33+
34+
public Task<T?> GetAsync<T>(StateKey key)
35+
{
36+
var httpContext = _httpContextAccessor.HttpContext ?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.CouldNotAccessHttpContext);
37+
if (httpContext.Request.Cookies.TryGetValue(key, out var value))
38+
{
39+
// Deserialize the value from the cookie. The cookie value is a JSON string.
40+
var deserializedValue = JsonSerializer.Deserialize<T>(value, _jsonSerializerOptions);
41+
return Task.FromResult(deserializedValue);
42+
}
43+
44+
return Task.FromResult<T?>(default);
45+
}
46+
47+
public Task<bool> TryGetAsync<T>(StateKey key, [NotNullWhen(true)] out T? value)
48+
{
49+
var httpContext = _httpContextAccessor.HttpContext ?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.CouldNotAccessHttpContext);
50+
if (httpContext.Request.Cookies.TryGetValue(key, out var cookieValue))
51+
{
52+
value = JsonSerializer.Deserialize<T>(cookieValue, _jsonSerializerOptions);
53+
return Task.FromResult(true);
54+
}
55+
56+
value = default;
57+
return Task.FromResult(false);
58+
}
59+
60+
private Task<StateKey> Set<T>(StateKey key, T value)
61+
{
62+
var httpContext = _httpContextAccessor.HttpContext ?? throw new InvalidOperationException(BankIdConstants.ErrorMessages.CouldNotAccessHttpContext);
63+
var serializedValue = JsonSerializer.Serialize(value, _jsonSerializerOptions);
64+
httpContext.Response.Cookies.Append(key, serializedValue, _cookieOptions);
65+
66+
return Task.FromResult(key);
67+
}
68+
69+
public Task<StateKey> SetAsync<T>(T value)
70+
{
71+
var key = Guid.NewGuid().ToString();
72+
var stateKey = new StateKey(key);
73+
74+
return Set(stateKey, value);
75+
}
76+
}

src/ActiveLogin.Authentication.BankId.AspNetCore/StateStorageExtensions.cs

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/ActiveLogin.Authentication.BankId.Core/IStateStorage.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ public interface IStateStorage
1414
Task<T?> GetAsync<T>(StateKey key);
1515
Task<bool> TryGetAsync<T>(StateKey key, [NotNullWhen(true)] out T? value);
1616
Task<StateKey> SetAsync<T>(T value);
17-
Task<StateKey> SetAsync<T>(T value, Action<StateKey, T> callback);
1817
}
1918

2019
public static class BankIdBuilderStateStorageExtensions

src/ActiveLogin.Authentication.BankId.Core/InMemoryStateStorage.cs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,10 @@
44

55
namespace ActiveLogin.Authentication.BankId.Core;
66

7-
public class InMemoryStateStorage(IMemoryCache cache, TimeSpan slidingExpiration) : IStateStorage
7+
public class InMemoryStateStorage(
8+
IMemoryCache cache,
9+
TimeSpan slidingExpiration
10+
) : IStateStorage
811
{
912
public InMemoryStateStorage(IMemoryCache cache) : this(cache, TimeSpan.FromMinutes(5)) { }
1013

@@ -26,25 +29,12 @@ public Task<bool> TryGetAsync<T>(StateKey key, [NotNullWhen(true)] out T? value)
2629
? Task.FromResult(true)
2730
: Task.FromResult(false);
2831
}
29-
private Task<StateKey> Set<T>(T value, MemoryCacheEntryOptions options)
32+
33+
public Task<StateKey> SetAsync<T>(T value)
3034
{
3135
var key = Guid.NewGuid().ToString();
3236
var stateKey = new StateKey(key);
33-
cache.Set(stateKey, value, options);
37+
cache.Set(stateKey, value, _memoryCacheEntryOptions);
3438
return Task.FromResult(stateKey);
3539
}
36-
public Task<StateKey> SetAsync<T>(T value) => Set(value, _memoryCacheEntryOptions);
37-
public Task<StateKey> SetAsync<T>(T value, Action<StateKey, T> evictionCallback)
38-
{
39-
var options = new MemoryCacheEntryOptions()
40-
{
41-
SlidingExpiration = _memoryCacheEntryOptions.SlidingExpiration
42-
};
43-
options.RegisterPostEvictionCallback((key, value, reason, state) =>
44-
{
45-
evictionCallback((StateKey)key, (T)value!);
46-
});
47-
48-
return Set(value, options);
49-
}
5040
}

test/ActiveLogin.Authentication.BankId.Core.Test/InMemoryStateStorage/InMemoryStateStorage_Tests.cs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,7 @@ public async Task Eviction_Works()
7575
// Arrange
7676
var value = "testState";
7777
var start = DateTime.Now;
78-
var key = await storage.SetAsync(value, (_, evictedValue) =>
79-
{
80-
Assert.Equal(value, evictedValue);
81-
});
78+
var key = await storage.SetAsync(value);
8279
if (!await storage.TryGetAsync<string>(key, out _))
8380
{
8481
Assert.Fail("Could not get value");

0 commit comments

Comments
 (0)