Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure AntiForgery in WebApp #295

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions application/account-management/WebApp/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export declare global {
APPLICATION_VERSION: string;
/* User locale */
LOCALE: string;
/* AntiForgery token */
XSRF_TOKEN: string;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import createClient from "openapi-fetch";
import type { paths } from "./api.generated";

const baseUrl = import.meta.env.PUBLIC_URL;
export const accountManagementApi = createClient<paths>({ baseUrl });
export const accountManagementApi = createClient<paths>({ baseUrl, headers: { "X-XSRF-TOKEN": import.meta.env.XSRF_TOKEN } });
32 changes: 32 additions & 0 deletions application/shared-kernel/ApiCore/ApiCoreConfiguration.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using System.Text.Json;
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Json;
using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.EntityFrameworkCore;
Expand Down Expand Up @@ -74,6 +76,9 @@ public static IServiceCollection AddApiCoreServices(this IServiceCollection serv
options.KnownProxies.Clear();
});

// Enable support for CSRF tokens and configure the header
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");

builder.AddServiceDefaults();

if (builder.Environment.IsDevelopment())
Expand Down Expand Up @@ -127,6 +132,22 @@ public static WebApplication AddApiCoreConfiguration<TDbContext>(this WebApplica

app.UseMiddleware<ModelBindingExceptionHandlerMiddleware>();


// Enable support for CSRF tokens
app.UseAntiforgery();

// Enable support for anti XSRF/CSRF on all mutation endpoints
app.Use((context, next) =>
{
var antiforgeryMetadata = context.GetEndpoint()?.Metadata.GetMetadata<IAntiforgeryMetadata>();
if (antiforgeryMetadata != null || !IsMutationMethod(context))
{
return next.Invoke();
}

return context.RequestServices.GetService<IAntiforgery>()!.ValidateRequestAsync(context);
});

// Configure track endpoint for Application Insights telemetry for PageViews and BrowserTimings
app.MapTrackEndpoints();

Expand All @@ -137,4 +158,15 @@ public static WebApplication AddApiCoreConfiguration<TDbContext>(this WebApplica

return app;
}

/// <summary>
/// Returns true if the request is a mutation method (POST, PUT, PATCH, DELETE)
/// </summary>
private static bool IsMutationMethod(HttpContext context)
{
return HttpMethods.IsPost(context.Request.Method) ||
HttpMethods.IsPut(context.Request.Method) ||
HttpMethods.IsPatch(context.Request.Method) ||
HttpMethods.IsDelete(context.Request.Method);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public static class TrackEndpoints
// </summary>
public static void MapTrackEndpoints(this IEndpointRouteBuilder routes)
{
routes.MapPost("/api/track", Track);
routes.MapPost("/api/track", Track).DisableAntiforgery();
}

[OpenApiIgnore]
Expand Down
12 changes: 10 additions & 2 deletions application/shared-kernel/ApiCore/Middleware/WebAppMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System.Security;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Antiforgery;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Json;
Expand All @@ -18,7 +19,9 @@ public sealed class WebAppMiddleware
public const string PublicUrlKey = "PUBLIC_URL";
public const string CdnUrlKey = "CDN_URL";
private const string Locale = "LOCALE";
private const string XsrfToken = "XSRF_TOKEN";
public const string ApplicationVersion = "APPLICATION_VERSION";
private readonly IAntiforgery _antiforgery;

private readonly string _cdnUrl;
private readonly StringValues _contentSecurityPolicy;
Expand All @@ -35,12 +38,14 @@ public WebAppMiddleware(
RequestDelegate next,
Dictionary<string, string> staticRuntimeEnvironment,
string htmlTemplatePath,
IOptions<JsonOptions> jsonOptions
IOptions<JsonOptions> jsonOptions,
IAntiforgery antiforgery
)
{
_next = next;
_staticRuntimeEnvironment = staticRuntimeEnvironment;
_htmlTemplatePath = htmlTemplatePath;
_antiforgery = antiforgery;
_jsonSerializerOptions = jsonOptions.Value.SerializerOptions;

VerifyRuntimeEnvironment(staticRuntimeEnvironment);
Expand Down Expand Up @@ -87,10 +92,13 @@ public Task InvokeAsync(HttpContext context)
{
if (context.Request.Path.ToString().StartsWith("/api/")) return _next(context);

var tokenSet = _antiforgery.GetAndStoreTokens(context);

var cultureFeature = context.Features.Get<IRequestCultureFeature>();
var userCulture = cultureFeature?.RequestCulture.Culture;

var requestEnvironmentVariables = new Dictionary<string, string> { { Locale, userCulture?.Name ?? "en-US" } };
var requestEnvironmentVariables = new Dictionary<string, string>
{ { Locale, userCulture?.Name ?? "en-US" }, { XsrfToken, tokenSet.RequestToken! } };

context.Response.Headers.Append("Content-Security-Policy", _contentSecurityPolicy);
return context.Response.WriteAsync(GetHtmlWithEnvironment(requestEnvironmentVariables));
Expand Down
Loading