Build / Build (push) Successful in 36s
Completion program R3 (docs/ROADMAP.md, 2026-10-17). - Services/TwoFactor.cs: TOTP (RFC 6238, 30 s, 6 digits, SHA-1, one step of slack, Otp.NET 1.4.1) and ten recovery codes (SHA-256 kept, pulled when used). RootUser.Totp holds the secret, the last step taken (moved only by a conditional update, so a code works once even for two sign-ins at the same moment) and the codes' hashes. - /clientapi/user/totp/setup (the password) answers the secret, its otpauth:// URI and a QR code as an SVG data URI (QRCoder 1.8.0); /totp/enable takes the app's first code, shows the recovery codes once and ends every other session (this device gets a new one); /totp/disable takes the password and a code. GET /clientapi/user/account says whether it is on and how many recovery codes are left. - Sign-in (/clientapi/user/login, the invitation login, /oauth/login) asks for LoginForm.OneTimeCode with a 428, and only once the password was right; the OAuth page has the field. Recovery by email leaves two-factor on. - PrivaPub admin reset-totp <root>: the way back for a login that lost its authenticator and its codes. - scenarios/accounts.sh checks it all, with codes computed in Python; the OAuth page with its antiforgery token. Verified: dotnet build PrivaPub.sln -c Release; the pasture (gts, mailpit): accounts 45/45. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WpVCoDeM1qUTRp4fXQdfof
20 lines
906 B
Plaintext
20 lines
906 B
Plaintext
@page "/oauth/login"
|
|
@model PrivaPub.Web.Pages.OAuth.LoginModel
|
|
@{
|
|
ViewData["Title"] = "Sign in";
|
|
}
|
|
<header><h1>Sign in to PrivaPub</h1></header>
|
|
<p class="meta">Sign in with your private login. You will then choose which persona the application acts as; the
|
|
application never sees the login.</p>
|
|
<form method="post">
|
|
<input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl">
|
|
<p><label>Username<br><input name="UserName" autocomplete="username" required autofocus></label></p>
|
|
<p><label>Password<br><input name="Password" type="password" autocomplete="current-password" required></label></p>
|
|
<p><label>Code from your authenticator app (only with two-factor sign-in)<br><input name="OneTimeCode" autocomplete="one-time-code" inputmode="text" maxlength="32"></label></p>
|
|
@if (Model.Error != default)
|
|
{
|
|
<p role="alert">@Model.Error</p>
|
|
}
|
|
<p><button type="submit">Sign in</button></p>
|
|
</form>
|