|
2 | 2 |
|
3 | 3 | var builder = WebApplication.CreateBuilder(args); |
4 | 4 | builder.Logging.ClearProviders(); |
5 | | -builder.Services.AddAntiforgery(options => options.HeaderName = "XSRF-TOKEN"); |
| 5 | + |
| 6 | +// "csrf" exercises the auto-injected cross-origin (Sec-Fetch) CSRF protection in isolation, |
| 7 | +// so the token-based antiforgery services/middleware are left out to avoid overriding its verdict. |
| 8 | +var scenario = builder.Configuration["scenario"] ?? "antiforgery"; |
| 9 | +var tokenAntiforgeryEnabled = !string.Equals(scenario, "csrf", StringComparison.OrdinalIgnoreCase); |
| 10 | + |
| 11 | +if (tokenAntiforgeryEnabled) |
| 12 | +{ |
| 13 | + builder.Services.AddAntiforgery(options => options.HeaderName = "XSRF-TOKEN"); |
| 14 | +} |
6 | 15 |
|
7 | 16 | var app = builder.Build(); |
8 | | -app.UseAntiforgery(); |
| 17 | + |
| 18 | +if (tokenAntiforgeryEnabled) |
| 19 | +{ |
| 20 | + app.UseAntiforgery(); |
| 21 | +} |
9 | 22 |
|
10 | 23 | app.MapGet("/", () => Results.Ok("hello world!")); |
11 | | -app.MapGet("/noOp", (HttpContext ctx, IAntiforgery antiforgery) => Results.Ok()); |
12 | 24 |
|
13 | | -// GET https://localhost:55471/auth |
14 | | -app.MapGet("/auth", (HttpContext ctx, IAntiforgery antiforgery) => |
15 | | -{ |
16 | | - var token = antiforgery.GetAndStoreTokens(ctx); |
17 | | - ctx.Response.Headers.Append("XSRF-TOKEN", token.RequestToken!); |
18 | | - return Results.Ok(); |
19 | | -}); |
| 25 | +// POST endpoint guarded only by the auto-injected cross-origin CSRF protection. |
| 26 | +// Sec-Fetch-Site: same-origin/none => 200; cross-site/same-site => 400. |
| 27 | +app.MapPost("/csrf", () => Results.Ok()); |
20 | 28 |
|
21 | | -// POST https://localhost:55471/validateToken |
22 | | -app.MapPost("/validateToken", async (HttpContext ctx, IAntiforgery antiforgery) => |
| 29 | +// Token-based antiforgery endpoints. These depend on IAntiforgery, which is only |
| 30 | +// registered when the token-based antiforgery services are added above. |
| 31 | +if (tokenAntiforgeryEnabled) |
23 | 32 | { |
24 | | - // HttpContext is expected to have 2 headers: |
25 | | - // 1) antiforgery token ("XSRF-TOKEN"); |
26 | | - // 2) cookie token ("Cookie") with value of `.AspNetCore.Antiforgery.<unique-sequence>=<cookie_header>` |
| 33 | + app.MapGet("/noOp", (HttpContext ctx, IAntiforgery antiforgery) => Results.Ok()); |
| 34 | + |
| 35 | + // GET https://localhost:55471/auth |
| 36 | + app.MapGet("/auth", (HttpContext ctx, IAntiforgery antiforgery) => |
| 37 | + { |
| 38 | + var token = antiforgery.GetAndStoreTokens(ctx); |
| 39 | + ctx.Response.Headers.Append("XSRF-TOKEN", token.RequestToken!); |
| 40 | + return Results.Ok(); |
| 41 | + }); |
| 42 | + |
| 43 | + // POST https://localhost:55471/validateToken |
| 44 | + app.MapPost("/validateToken", async (HttpContext ctx, IAntiforgery antiforgery) => |
| 45 | + { |
| 46 | + // HttpContext is expected to have 2 headers: |
| 47 | + // 1) antiforgery token ("XSRF-TOKEN"); |
| 48 | + // 2) cookie token ("Cookie") with value of `.AspNetCore.Antiforgery.<unique-sequence>=<cookie_header>` |
27 | 49 |
|
28 | | - await antiforgery.ValidateRequestAsync(ctx); |
29 | | - return Results.Ok(); |
30 | | -}); |
| 50 | + await antiforgery.ValidateRequestAsync(ctx); |
| 51 | + return Results.Ok(); |
| 52 | + }); |
| 53 | +} |
31 | 54 |
|
32 | 55 | await app.StartAsync(); |
33 | 56 | Console.WriteLine("Application started."); |
|
0 commit comments