|
6 | 6 | using Microsoft.AspNetCore.Http.Features; |
7 | 7 | using Microsoft.Extensions.DependencyInjection; |
8 | 8 | using Microsoft.Extensions.Logging; |
| 9 | +using Microsoft.Extensions.Logging.Abstractions; |
| 10 | +using Microsoft.Extensions.Logging.Testing; |
| 11 | +using Microsoft.Extensions.ObjectPool; |
9 | 12 | using Microsoft.Extensions.Options; |
10 | 13 | using Moq; |
11 | | -using Microsoft.Extensions.Logging.Testing; |
12 | 14 |
|
13 | 15 | namespace Microsoft.AspNetCore.Antiforgery.Internal; |
14 | 16 |
|
@@ -156,6 +158,70 @@ public void GetTokens_ExistingInvalidCookieToken_GeneratesANewCookieTokenAndANew |
156 | 158 | Assert.Equal(context.TestTokenSet.FormTokenString, antiforgeryFeature.NewRequestTokenString); |
157 | 159 | } |
158 | 160 |
|
| 161 | + [Fact] |
| 162 | + public async Task ValidateRequestAsync_DoesNotThrow_OnBuiltHttpContext() |
| 163 | + { |
| 164 | + var serviceCollection = new ServiceCollection() |
| 165 | + .AddSingleton<ILoggerFactory>(NullLoggerFactory.Instance) |
| 166 | + .AddSingleton(typeof(ILogger<>), typeof(NullLogger<>)) |
| 167 | + .AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>() |
| 168 | + .AddAntiforgery(); |
| 169 | + |
| 170 | + serviceCollection.Configure<AntiforgeryOptions>(options => { |
| 171 | + options.HeaderName = "XSRF-TOKEN"; |
| 172 | + }); |
| 173 | + |
| 174 | + var services = serviceCollection.BuildServiceProvider(); |
| 175 | + var antiforgery = services.GetRequiredService<IAntiforgery>(); |
| 176 | + |
| 177 | + var cookieName = services.GetRequiredService<IOptions<AntiforgeryOptions>>().Value.Cookie.Name!; |
| 178 | + |
| 179 | + var incomingRequestWithUserCtx = PrepareRequest(antiforgery, cookieName, withIdentity: true); |
| 180 | + |
| 181 | + // act - no exception expected |
| 182 | + await antiforgery.ValidateRequestAsync(incomingRequestWithUserCtx); |
| 183 | + } |
| 184 | + |
| 185 | + private static HttpContext PrepareRequest(IAntiforgery antiforgery, string cookieName, bool withIdentity = false) |
| 186 | + { |
| 187 | + // Simulate initial request to get tokens and capture Set-Cookie header |
| 188 | + var ctx = new DefaultHttpContext(); |
| 189 | + if (withIdentity) |
| 190 | + { |
| 191 | + ctx.User = new ClaimsPrincipal(GetAuthenticatedIdentity("the-user")); |
| 192 | + } |
| 193 | + |
| 194 | + var tokens = antiforgery.GetAndStoreTokens(ctx); |
| 195 | + |
| 196 | + // Extract the Set-Cookie header from the response (written by GetAndStoreTokens) |
| 197 | + var setCookieHeader = ctx.Response.Headers["Set-Cookie"]; |
| 198 | + var cookieValue = setCookieHeader |
| 199 | + .FirstOrDefault(h => h!.StartsWith(cookieName + "=", StringComparison.InvariantCultureIgnoreCase))? |
| 200 | + .Split(';')[0] // e.g. XSRF-TOKEN=abc123... |
| 201 | + .Substring(cookieName.Length + 1); // Just the value |
| 202 | + if (cookieValue == null) |
| 203 | + { |
| 204 | + throw new InvalidOperationException("Failed to extract antiforgery cookie."); |
| 205 | + } |
| 206 | + |
| 207 | + // Set headers on your test context |
| 208 | + var context = new DefaultHttpContext(); |
| 209 | + context.Request.Headers["XSRF-TOKEN"] = tokens.RequestToken; |
| 210 | + context.Request.Headers["Cookie"] = $"{cookieName}={cookieValue}"; |
| 211 | + if (withIdentity) |
| 212 | + { |
| 213 | + context.User = ctx.User; |
| 214 | + } |
| 215 | + |
| 216 | + return context; |
| 217 | + |
| 218 | + ClaimsIdentity GetAuthenticatedIdentity(string identityUsername) |
| 219 | + { |
| 220 | + var claim = new Claim(ClaimsIdentity.DefaultNameClaimType, identityUsername); |
| 221 | + return new ClaimsIdentity(new[] { claim }, "Some-Authentication"); |
| 222 | + } |
| 223 | + } |
| 224 | + |
159 | 225 | [Fact] |
160 | 226 | public void GetTokens_ExistingInvalidCookieToken_SwallowsExceptions() |
161 | 227 | { |
|
0 commit comments