Skip to content

feat(portal): two-factor step at sign-in and password change - #89

Merged
kipavy merged 1 commit into
mainfrom
feat/portal-two-factor-signin
Oct 11, 2026
Merged

kipavy merged 1 commit into
mainfrom
feat/portal-two-factor-signin

Conversation

@kipavy

@kipavy kipavy commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Portal half of optional two-factor sign-in (VoltiusApp/server#105, client VoltiusApp/voltius#615).

Without this, an account that turns two-factor on cannot sign in to the portal: /v1/auth/login answers 428 TWO_FACTOR_REQUIRED and the page printed the JSON body as the error.

Changes

  • Sign-in: when the server asks for a second factor the card switches to a code field, with "Use a recovery code" and Back. It finishes through POST /v1/auth/login/2fa. A server that only offers recovery codes (methods: ["recovery"]) gets only that field. A pending sign-in that ran past its five minutes goes back to the password form with a message.
  • Change master password: asks for a code when two_factor_enabled is set on /v1/auth/me, and sends it with the change.
  • ApiError.body parses the {error: CODE} body; twoFactorMessage turns the four codes into text.

Safe to deploy before or after the server: a server without the feature never answers 428 and never sets two_factor_enabled.

Checked

pnpm exec tsc --noEmit in portal/. Not driven in a browser against a live server.

Enrolment stays in the desktop and mobile app; the portal only needs to get an enrolled account in.

An account that turned on two-factor sign-in (VoltiusApp/server#105)
gets 428 TWO_FACTOR_REQUIRED from /v1/auth/login. The portal showed
that as a raw JSON error, so those accounts could not reach billing.

Sign-in now moves to a code step when the server asks for one and
finishes through /v1/auth/login/2fa, with a switch to a recovery code.
Changing the master password asks for a code when the account has
two-factor on, since the server refuses the change without one.

ApiError exposes the parsed `{error: CODE}` body so a refusal can be
told apart without matching on text.
@vercel

vercel Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
voltius Ready Ready Preview Oct 11, 2026 2:09pm UTC
voltius-portal Ready Ready Preview Oct 11, 2026 2:09pm UTC

@kipavy
kipavy merged commit e1beb90 into main Oct 11, 2026
4 checks passed

This branch was successfully deployed

2 active deployments
Preview – voltius — 6a8e6ba8 Deployed Oct 11, 2026 by vercel[bot]
Preview – voltius-portal — 6a8e6ba8 Deployed Oct 11, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant