-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use a cookie to prevent FormFlow journey theft (#1268)
- Loading branch information
Showing
5 changed files
with
111 additions
and
9 deletions.
There are no files selected for viewing
8 changes: 0 additions & 8 deletions
8
...eachingRecordSystem.AuthorizeAccess/Infrastructure/FormFlow/DummyCurrentUserIdProvider.cs
This file was deleted.
Oops, something went wrong.
12 changes: 12 additions & 0 deletions
12
...ordSystem.AuthorizeAccess/Infrastructure/FormFlow/FormFlowSessionCurrentUserIdProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
using TeachingRecordSystem.SupportUi.Infrastructure.FormFlow; | ||
|
||
namespace TeachingRecordSystem.AuthorizeAccess.Infrastructure.FormFlow; | ||
|
||
public class FormFlowSessionCurrentUserIdProvider(IHttpContextAccessor httpContextAccessor) : ICurrentUserIdProvider | ||
{ | ||
public string GetCurrentUserId() | ||
{ | ||
var httpContext = httpContextAccessor.HttpContext ?? throw new InvalidOperationException("No HttpContext."); | ||
return httpContext.Features.Get<FormFlowSessionIdFeature>()?.SessionId ?? throw new InvalidOperationException($"No {nameof(FormFlowSessionIdFeature)} set."); | ||
} | ||
} |
6 changes: 6 additions & 0 deletions
6
.../TeachingRecordSystem.AuthorizeAccess/Infrastructure/FormFlow/FormFlowSessionIdFeature.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
namespace TeachingRecordSystem.AuthorizeAccess.Infrastructure.FormFlow; | ||
|
||
public class FormFlowSessionIdFeature(string sessionId) | ||
{ | ||
public string SessionId { get; } = sessionId; | ||
} |
90 changes: 90 additions & 0 deletions
90
...TeachingRecordSystem.AuthorizeAccess/Infrastructure/FormFlow/FormFlowSessionMiddleware.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
using System.Diagnostics.CodeAnalysis; | ||
using System.Security.Cryptography; | ||
using Microsoft.AspNetCore.DataProtection; | ||
|
||
namespace TeachingRecordSystem.AuthorizeAccess.Infrastructure.FormFlow; | ||
|
||
public class FormFlowSessionMiddleware(RequestDelegate next, IDataProtectionProvider dataProtectionProvider) | ||
{ | ||
private const string CookieName = "ffsessid"; | ||
|
||
private readonly IDataProtector _dataProtector = dataProtectionProvider.CreateProtector(nameof(FormFlowSessionMiddleware)); | ||
|
||
private readonly CookieOptions _cookieOptions = new() | ||
{ | ||
HttpOnly = true, | ||
IsEssential = true, | ||
Secure = true, | ||
}; | ||
|
||
public async Task Invoke(HttpContext context) | ||
{ | ||
if (!TryExtractSessionIdFromRequest(context, out var sessionId)) | ||
{ | ||
sessionId = AddNewSessionIdToResponse(context); | ||
} | ||
|
||
context.Features.Set(new FormFlowSessionIdFeature(sessionId)); | ||
|
||
try | ||
{ | ||
await next(context); | ||
} | ||
finally | ||
{ | ||
context.Features.Set<FormFlowSessionIdFeature>(null); | ||
} | ||
} | ||
|
||
private string AddNewSessionIdToResponse(HttpContext context) | ||
{ | ||
var sessionId = CreateSessionId(); | ||
var cookieValue = _dataProtector.Protect(sessionId); | ||
|
||
context.Response.OnStarting( | ||
state => | ||
{ | ||
var (context, cookieValue) = ((HttpContext, string))state; | ||
context.Response.Cookies.Append(CookieName, cookieValue, _cookieOptions); | ||
return Task.CompletedTask; | ||
}, | ||
(context, cookieValue)); | ||
|
||
return sessionId.ToString(); | ||
|
||
static string CreateSessionId() | ||
{ | ||
Span<byte> guidBytes = stackalloc byte[16]; | ||
RandomNumberGenerator.Fill(guidBytes); | ||
return new Guid(guidBytes).ToString(); | ||
} | ||
} | ||
|
||
private bool TryExtractSessionIdFromRequest(HttpContext context, [NotNullWhen(true)] out string? sessionId) | ||
{ | ||
if (context.Request.Cookies.TryGetValue(CookieName, out var cookieValue)) | ||
{ | ||
try | ||
{ | ||
var bytes = _dataProtector.Unprotect(cookieValue); | ||
sessionId = new Guid(bytes).ToString(); | ||
return true; | ||
} | ||
catch (CryptographicException) | ||
{ | ||
} | ||
} | ||
|
||
sessionId = null; | ||
return false; | ||
} | ||
} | ||
|
||
public class FormFlowSessionMiddlewareStartupFilter : IStartupFilter | ||
{ | ||
public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next) => app => | ||
{ | ||
app.UseMiddleware<FormFlowSessionMiddleware>(); | ||
next(app); | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters