Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,16 @@ public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEn
return TypedResults.LocalRedirect($"~/{returnUrl}");
});

accountGroup.MapPost("/PasskeyCreationOptions", async (
accountGroup.MapPost("/PasskeyCreationOptions", [RequireAntiforgeryToken] async (
HttpContext context,
[FromServices] UserManager<ApplicationUser> userManager,
[FromServices] SignInManager<ApplicationUser> signInManager,
[FromServices] IAntiforgery antiforgery) =>
[FromServices] SignInManager<ApplicationUser> signInManager) =>
{
await antiforgery.ValidateRequestAsync(context);
var antiforgeryValidationFeature = context.Features.Get<IAntiforgeryValidationFeature>();
if (antiforgeryValidationFeature is not { IsValid: true })
{
return Results.BadRequest(antiforgeryValidationFeature?.Error?.Message ?? "Antiforgery validation failed.");
}

var user = await userManager.GetUserAsync(context.User);
if (user is null)
Expand All @@ -75,14 +78,17 @@ public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEn
return TypedResults.Content(optionsJson, contentType: "application/json");
});

accountGroup.MapPost("/PasskeyRequestOptions", async (
accountGroup.MapPost("/PasskeyRequestOptions", [RequireAntiforgeryToken] async (
HttpContext context,
[FromServices] UserManager<ApplicationUser> userManager,
[FromServices] SignInManager<ApplicationUser> signInManager,
[FromServices] IAntiforgery antiforgery,
[FromQuery] string? username) =>
{
await antiforgery.ValidateRequestAsync(context);
var antiforgeryValidationFeature = context.Features.Get<IAntiforgeryValidationFeature>();
if (antiforgeryValidationFeature is not { IsValid: true })
{
return Results.BadRequest(antiforgeryValidationFeature?.Error?.Message ?? "Antiforgery validation failed.");
}

var user = string.IsNullOrEmpty(username) ? null : await userManager.FindByNameAsync(username);
var optionsJson = await signInManager.MakePasskeyRequestOptionsAsync(user);
Expand Down Expand Up @@ -111,11 +117,17 @@ public static IEndpointConventionBuilder MapAdditionalIdentityEndpoints(this IEn
var loggerFactory = endpoints.ServiceProvider.GetRequiredService<ILoggerFactory>();
var downloadLogger = loggerFactory.CreateLogger("DownloadPersonalData");

manageGroup.MapPost("/DownloadPersonalData", async (
manageGroup.MapPost("/DownloadPersonalData", [RequireAntiforgeryToken] async (
HttpContext context,
[FromServices] UserManager<ApplicationUser> userManager,
[FromServices] AuthenticationStateProvider authenticationStateProvider) =>
{
var antiforgeryValidationFeature = context.Features.Get<IAntiforgeryValidationFeature>();
if (antiforgeryValidationFeature is not { IsValid: true })
{
return Results.BadRequest(antiforgeryValidationFeature?.Error?.Message ?? "Antiforgery validation failed.");
}

var user = await userManager.GetUserAsync(context.User);
if (user is null)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@

<div>
<form @formname="disable-2fa" @onsubmit="OnSubmitAsync" method="post">
<AntiforgeryToken />
<button class="btn btn-danger" type="submit">Disable 2FA</button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
<div class="row">
<div class="col-xl-6">
<form @onsubmit="OnSendEmailVerificationAsync" @formname="send-verification" id="send-verification-form" method="post">
<AntiforgeryToken />
</form>
<EditForm Model="Input" FormName="change-email" OnValidSubmit="OnValidSubmitAsync" method="post">
<DataAnnotationsValidator />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@
@if (showRemoveButton)
{
<form @formname="@($"remove-login-{login.LoginProvider}")" @onsubmit="OnSubmitAsync" method="post">
<AntiforgeryToken />
<div>
<input type="hidden" name="@nameof(LoginProvider)" value="@login.LoginProvider" />
<input type="hidden" name="@nameof(ProviderKey)" value="@login.ProviderKey" />
Expand All @@ -48,7 +47,6 @@
<h4>Add another service to log in.</h4>
<hr />
<form class="form-horizontal" action="Account/Manage/LinkExternalLogin" method="post">
<AntiforgeryToken />
<div>
<p>
@foreach (var provider in otherLogins)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ else
</div>
<div>
<form @formname="generate-recovery-codes" @onsubmit="OnSubmitAsync" method="post">
<AntiforgeryToken />
<button class="btn btn-danger" type="submit">Generate Recovery Codes</button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
var credentialId = Base64Url.EncodeToString(passkey.CredentialId);
}
<form @formname="@($"update-passkey-{credentialId}")" @onsubmit="UpdatePasskey" method="post">
<AntiforgeryToken />
<div>
<input type="hidden" name="CredentialId" value="@credentialId" />
<button type="submit" name="Action" value="rename" class="btn btn-primary" title="Rename this passkey">Rename</button>
Expand All @@ -50,7 +49,6 @@ else
}

<form @formname="add-passkey" @onsubmit="AddPasskey" method="post">
<AntiforgeryToken />
@if (currentPasskeys is { Count: >= MaxPasskeyCount })
{
<p class="text-danger">You have reached the maximum number of allowed passkeys. Please delete one before adding a new one.</p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
<strong>Deleting this data will permanently remove your account, and this cannot be recovered.</strong>
</p>
<form action="Account/Manage/DownloadPersonalData" method="post">
<AntiforgeryToken />
<button class="btn btn-primary" type="submit">Download</button>
</form>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
</div>
<div>
<form @formname="reset-authenticator" @onsubmit="OnSubmitAsync" method="post">
<AntiforgeryToken />
<button class="btn btn-danger" type="submit">Reset authenticator key</button>
</form>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
if (isMachineRemembered)
{
<form style="display: inline-block" @formname="forget-browser" @onsubmit="OnSubmitForgetBrowserAsync" method="post">
<AntiforgeryToken />
<button type="submit" class="btn btn-primary">Forget this browser</button>
</form>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ else
{
<form class="form-horizontal" action="Account/PerformExternalLogin" method="post">
<div>
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@ReturnUrl" />
<p>
@foreach (var provider in externalLogins)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,11 @@
@using Microsoft.AspNetCore.Antiforgery
@inject IServiceProvider Services

<button type="submit" name="__passkeySubmit" @attributes="AdditionalAttributes" formnovalidate>@ChildContent</button>
<button type="submit" name="__passkeySubmit" @attributes="AdditionalAttributes" formnovalidate>@ChildContent</button>
<passkey-submit
operation="@Operation"
name="@Name"
email-name="@EmailName"
request-token-name="@tokens?.HeaderName"
request-token-value="@tokens?.RequestToken">
email-name="@EmailName">
</passkey-submit>

@code {
private AntiforgeryTokenSet? tokens;

[CascadingParameter]
private HttpContext HttpContext { get; set; } = default!;

[Parameter]
[EditorRequired]
public PasskeyOperation Operation { get; set; }
Expand All @@ -32,9 +22,4 @@

[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }

protected override void OnInitialized()
{
tokens = Services.GetService<IAntiforgery>()?.GetTokens(HttpContext);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,19 @@ async function fetchWithErrorHandling(url, options = {}) {
return response;
}

async function createCredential(headers, signal) {
async function createCredential(signal) {
const optionsResponse = await fetchWithErrorHandling('/Account/PasskeyCreationOptions', {
method: 'POST',
headers,
signal,
});
const optionsJson = await optionsResponse.json();
const options = PublicKeyCredential.parseCreationOptionsFromJSON(optionsJson);
return await navigator.credentials.create({ publicKey: options, signal });
}

async function requestCredential(email, mediation, headers, signal) {
async function requestCredential(email, mediation, signal) {
const optionsResponse = await fetchWithErrorHandling(`/Account/PasskeyRequestOptions?username=${email}`, {
method: 'POST',
headers,
signal,
});
const optionsJson = await optionsResponse.json();
Expand All @@ -48,8 +46,6 @@ customElements.define('passkey-submit', class extends HTMLElement {
operation: this.getAttribute('operation'),
name: this.getAttribute('name'),
emailName: this.getAttribute('email-name'),
requestTokenName: this.getAttribute('request-token-name'),
requestTokenValue: this.getAttribute('request-token-value'),
};

this.internals.form.addEventListener('submit', (event) => {
Expand All @@ -71,16 +67,12 @@ customElements.define('passkey-submit', class extends HTMLElement {
throw new Error('Some passkey features are missing. Please update your browser.');
}

const headers = {
[this.attrs.requestTokenName]: this.attrs.requestTokenValue,
};

if (this.attrs.operation === 'Create') {
return await createCredential(headers, signal);
return await createCredential(signal);
} else if (this.attrs.operation === 'Request') {
const email = new FormData(this.internals.form).get(this.attrs.emailName);
const mediation = useConditionalMediation ? 'conditional' : undefined;
return await requestCredential(email, mediation, headers, signal);
return await requestCredential(email, mediation, signal);
} else {
throw new Error(`Unknown passkey operation '${this.attrs.operation}'.`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
</div>
<div class="nav-item px-3">
<form action="Account/Logout" method="post">
<AntiforgeryToken />
<input type="hidden" name="ReturnUrl" value="@currentUrl" />
<button type="submit" class="nav-link">
<span class="bi bi-arrow-bar-left-nav-menu" aria-hidden="true"></span> Logout
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Net;
using System.Text.Json;
using Microsoft.AspNetCore.BrowserTesting;
using Microsoft.AspNetCore.InternalTesting;
using Templates.Test.Helpers;
Comment thread
DeagleGross marked this conversation as resolved.
Expand Down Expand Up @@ -111,4 +110,6 @@ public async Task BlazorWebTemplateLocalhostTld_GeneratesDnsCompliantHostnames(s
await project.VerifyLaunchSettings(expectedLaunchProfileNames);
await project.VerifyDnsCompliantHostname(expectedHostname);
}

}

Loading