Background and Motivation
The Identity UI Manage/SetPassword page adds a password to an account that does not have one, which
is how someone who signed up with an external login gets a password. Right now the only thing it checks
is the sign-in cookie.
I want the page to ask the user to confirm with a credential the account already has before it accepts a
new password, the same gate the Blazor template got in #68522, reauthentication before adding a passkey
or setting a password. The views need somewhere to read the confirmation state from and two handlers to
post to, and in Identity UI that means the page model.
Proposed API
Identical additions in the V4 and V5 namespaces, since Identity UI ships both.
namespace Microsoft.AspNetCore.Identity.UI.V5.Pages.Account.Manage.Internal;
public abstract class SetPasswordModel : PageModel
{
public SetPasswordModel.InputModel Input { get; set; }
public string? StatusMessage { get; set; }
+ public bool IsReauthenticated { get; set; }
+ public IList<UserLoginInfo>? CurrentLogins { get; set; }
public virtual Task<IActionResult> OnGetAsync();
public virtual Task<IActionResult> OnPostAsync();
+ public virtual Task<IActionResult> OnPostReauthenticateAsync(string provider);
+ public virtual Task<IActionResult> OnGetReauthenticationCallbackAsync();
}
namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal;
public abstract class SetPasswordModel : PageModel
{
public SetPasswordModel.InputModel Input { get; set; }
public string? StatusMessage { get; set; }
+ public bool IsReauthenticated { get; set; }
+ public IList<UserLoginInfo>? CurrentLogins { get; set; }
public virtual Task<IActionResult> OnGetAsync();
public virtual Task<IActionResult> OnPostAsync();
+ public virtual Task<IActionResult> OnPostReauthenticateAsync(string provider);
+ public virtual Task<IActionResult> OnGetReauthenticationCallbackAsync();
}
CurrentLogins is nullable to separate "this store cannot support the feature" from "this account has no
linked logins". The two need different messages: the first is something the app developer has to fix, the
second is something the user can fix.
Usage Examples
Nobody calls these from C#. They are reached from the view, which is the call site reviewers should judge:
@if (!Model.IsReauthenticated)
{
@if (Model.CurrentLogins is null)
{
<p class="text-danger">Setting a password requires a user store that supports security stamps.</p>
}
else
{
<form asp-page-handler="Reauthenticate" method="post">
@foreach (var login in Model.CurrentLogins)
{
<button type="submit" name="provider" value="@login.LoginProvider">
Confirm with @login.ProviderDisplayName
</button>
}
</form>
}
}
OnGetReauthenticationCallbackAsync is the return leg of the provider challenge, reached as
?handler=ReauthenticationCallback.
Risks
Behaviour change on an existing path: yes, two of them. A user store that does not support security
stamps can no longer set a password through this page, because the confirmation is bound to the stamp and
there is nothing to bind to. The page says so explicitly rather than failing as a refused confirmation.
Separately, an account whose linked provider is no longer configured in the app has nothing to confirm
with and cannot set a password here.
Background and Motivation
The Identity UI
Manage/SetPasswordpage adds a password to an account that does not have one, whichis how someone who signed up with an external login gets a password. Right now the only thing it checks
is the sign-in cookie.
I want the page to ask the user to confirm with a credential the account already has before it accepts a
new password, the same gate the Blazor template got in #68522, reauthentication before adding a passkey
or setting a password. The views need somewhere to read the confirmation state from and two handlers to
post to, and in Identity UI that means the page model.
Proposed API
Identical additions in the V4 and V5 namespaces, since Identity UI ships both.
namespace Microsoft.AspNetCore.Identity.UI.V5.Pages.Account.Manage.Internal; public abstract class SetPasswordModel : PageModel { public SetPasswordModel.InputModel Input { get; set; } public string? StatusMessage { get; set; } + public bool IsReauthenticated { get; set; } + public IList<UserLoginInfo>? CurrentLogins { get; set; } public virtual Task<IActionResult> OnGetAsync(); public virtual Task<IActionResult> OnPostAsync(); + public virtual Task<IActionResult> OnPostReauthenticateAsync(string provider); + public virtual Task<IActionResult> OnGetReauthenticationCallbackAsync(); }namespace Microsoft.AspNetCore.Identity.UI.V4.Pages.Account.Manage.Internal; public abstract class SetPasswordModel : PageModel { public SetPasswordModel.InputModel Input { get; set; } public string? StatusMessage { get; set; } + public bool IsReauthenticated { get; set; } + public IList<UserLoginInfo>? CurrentLogins { get; set; } public virtual Task<IActionResult> OnGetAsync(); public virtual Task<IActionResult> OnPostAsync(); + public virtual Task<IActionResult> OnPostReauthenticateAsync(string provider); + public virtual Task<IActionResult> OnGetReauthenticationCallbackAsync(); }CurrentLoginsis nullable to separate "this store cannot support the feature" from "this account has nolinked logins". The two need different messages: the first is something the app developer has to fix, the
second is something the user can fix.
Usage Examples
Nobody calls these from C#. They are reached from the view, which is the call site reviewers should judge:
OnGetReauthenticationCallbackAsyncis the return leg of the provider challenge, reached as?handler=ReauthenticationCallback.Risks
Behaviour change on an existing path: yes, two of them. A user store that does not support security
stamps can no longer set a password through this page, because the confirmation is bound to the stamp and
there is nothing to bind to. The page says so explicitly rather than failing as a refused confirmation.
Separately, an account whose linked provider is no longer configured in the app has nothing to confirm
with and cannot set a password here.