Skip to content

Commit

Permalink
Use a cookie to prevent FormFlow journey theft (#1268)
Browse files Browse the repository at this point in the history
  • Loading branch information
gunndabad authored Apr 9, 2024
1 parent 7934bfe commit 4220882
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 9 deletions.

This file was deleted.

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.");
}
}
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;
}
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);
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -151,11 +151,13 @@ static SecurityKey LoadKey(string configurationValue)
.AddTransient<AuthorizeAccessLinkGenerator, RoutingAuthorizeAccessLinkGenerator>()
.AddTransient<FormFlowJourneySignInHandler>()
.AddTransient<MatchToTeachingRecordAuthenticationHandler>()
.AddHttpContextAccessor()
.AddSingleton<IStartupFilter, FormFlowSessionMiddlewareStartupFilter>()
.AddFormFlow(options =>
{
options.JourneyRegistry.RegisterJourney(SignInJourneyState.JourneyDescriptor);
})
.AddSingleton<ICurrentUserIdProvider, DummyCurrentUserIdProvider>()
.AddSingleton<ICurrentUserIdProvider, FormFlowSessionCurrentUserIdProvider>()
.AddTransient<SignInJourneyHelper>()
.AddSingleton<ITagHelperInitializer<FormTagHelper>, FormTagHelperInitializer>()
.AddPersonSearch();
Expand Down

0 comments on commit 4220882

Please sign in to comment.