Voyti
Stateless Client
A REST API for a browser-based single-page application, or SPA (or any other stateless client),
built on voyti-api’s bearer-token infrastructure: credential login/logout, self-registration,
password reset, own-profile and own-sessions management, plus admin RBAC and audit-log endpoints.
Installation
Authentication
POST v1/auth/login verifies a username/email + password and returns
{"status": "ok", "token": "..."} - a bearer token for the same
Authorization: Bearer <token> scheme voyti-api uses everywhere else. It replicates the exact
event sequence core’s SessionController::login() dispatches
(BeforeLoginEvent/FailedLoginEvent/AfterLoginEvent), so voyti-lockout and
audit-log/session listeners work unmodified regardless of whether a login came from the HTML app or
this API. If the account has 2FA enabled, login instead returns a challenge - see below.
Public
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-auth-login |
POST |
v1/auth/login |
Credential login. Returns a bearer token, or a challenge_required body if 2FA is enabled for the account |
voyti/api-v1-auth-register |
POST |
v1/auth/register |
Self-registration |
voyti/api-v1-auth-register-confirm |
GET |
v1/auth/register/confirm/{id}/{code} |
Confirm an emailed registration code |
voyti/api-v1-auth-register-resend |
POST |
v1/auth/register/resend |
Resend the confirmation email |
voyti/api-v1-auth-password-reset-request |
POST |
v1/auth/password-reset/request |
Request a password-recovery email |
voyti/api-v1-auth-password-reset-confirm |
POST |
v1/auth/password-reset/confirm |
Set a new password from an emailed recovery code |
Authenticated
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-auth-logout |
POST |
v1/auth/logout |
Revoke the calling bearer token |
voyti/api-v1-auth-me-show |
GET |
v1/auth/me |
The current account’s profile |
voyti/api-v1-auth-me-update |
PATCH |
v1/auth/me |
Update username/password directly; an email change is routed through the confirmation flow instead of applied immediately |
voyti/api-v1-auth-sessions-index |
GET |
v1/auth/sessions |
List the account’s active bearer tokens |
voyti/api-v1-auth-sessions-terminate |
DELETE |
v1/auth/sessions/{id} |
Revoke one bearer token by its stored hash |
Admin
Reuse the same admin-gated group voyti-api-user’s endpoints do
(AccessRuleMiddleware enforces administratorPermissionName). RBAC mirrors core’s HTML admin
screen: {itemType} is role or permission. Per-user assignment management isn’t included here
yet - it’s a separate sub-resource, not a property of the item itself.
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-audit-log-index |
GET |
v1/audit-log |
Paginated, filterable audit-log listing |
voyti/api-v1-rbac-index |
GET |
v1/rbac/{itemType} |
List roles or permissions |
voyti/api-v1-rbac-create |
POST |
v1/rbac/{itemType} |
Create a role or permission |
voyti/api-v1-rbac-update |
PATCH |
v1/rbac/{itemType}/{name} |
Update name/description/rule/children |
voyti/api-v1-rbac-delete |
DELETE |
v1/rbac/{itemType}/{name} |
Delete a role or permission |
Two-factor authentication
When voyti-2fa is installed, a login for an account with 2FA
enabled returns a challenge instead of a token:
{ "status": "challenge_required", "challengeToken": "...", "method": "totp", "isCodeBased": true, "expiresIn": 300 }Submit the code (or a backup code, or a WebAuthn-style payload/domain pair for
client-collected methods) along with the challengeToken to receive the real bearer token:
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-auth-challenge-verify |
POST |
v1/auth/challenge/verify |
Verify the 2FA code (or backup code, or WebAuthn-style payload) and receive the real bearer token |
Enrollment and management are also available - enabling/disabling a code-based method and regenerating backup codes, mirroring the same re-authentication rule as the HTML settings page. Enable returns a fresh set of backup codes directly in the response, since a JSON caller has no follow-up reveal page to redirect to:
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-2fa-status |
GET |
v1/2fa |
Whether 2FA is enabled, which method, available methods, and whether unused backup codes remain |
voyti/api-v1-2fa-enable |
POST |
v1/2fa/enable |
Enable a code-based method; returns a fresh set of backup codes |
voyti/api-v1-2fa-disable |
POST |
v1/2fa/disable |
Disable 2FA (re-authentication required) |
voyti/api-v1-2fa-backup-codes-regenerate |
POST |
v1/2fa/backup-codes/regenerate |
Invalidate existing backup codes and issue a fresh set (re-authentication required) |
A code-based method’s own setup step must run first, or enable has nothing to verify a submitted
code against - mirroring each package’s HTML setup screen. When
voyti-2fa-totp is installed, setup/renew issue the QR code and secret;
setup reuses the account’s existing secret, renew always issues a fresh one. When
voyti-2fa-email is installed, send-code emails a fresh code (never
returned in the response - only the mailer sees it):
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-2fa-totp-setup |
GET |
v1/2fa/totp/setup |
QR code and secret, reusing the account’s existing pending secret if one exists |
voyti/api-v1-2fa-totp-renew |
POST |
v1/2fa/totp/renew |
Always issues a fresh QR code and secret |
voyti/api-v1-2fa-email-send-code |
POST |
v1/2fa/email/send-code |
Emails a fresh verification code |
Registering a client-collected method (WebAuthn) isn’t done through the generic action above - it
runs through its own dedicated ceremony. When voyti-2fa-webauthn is
installed, the pending challenge normally kept in the session is instead persisted on the account’s
own (not-yet-enabled) two-factor row between the two requests, since a bearer-token caller has no
session continuity across them:
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-2fa-webauthn-register-start |
POST |
v1/2fa/webauthn/register/start |
Builds the navigator.credentials.create() options and stashes the ceremony challenge |
voyti/api-v1-2fa-webauthn-register-finish |
POST |
v1/2fa/webauthn/register/finish |
Verifies the attestation response, enables 2FA, and returns fresh backup codes |
GDPR
When voyti-gdpr is installed, self-service data export and account
anonymization become available, delegating to that package’s own GdprExportService and
AnonymizeUserService:
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-gdpr-export |
GET |
v1/gdpr/export |
Data export as JSON |
voyti/api-v1-gdpr-anonymize |
POST |
v1/gdpr/anonymize |
Password-confirmed anonymization; also revokes every bearer token for the account |
Social login
When voyti-social-auth is installed, a popup-based OAuth2 flow becomes
available. Unlike the HTML app’s social login, this doesn’t establish a session in the SPA itself -
the popup completes the provider round-trip, then redirects to redirectUrl with a short-lived,
single-use code rather than the bearer token directly, so the token never ends up in a URL or
browser history entry:
| Route name | Method | Path | Purpose |
|---|---|---|---|
voyti/api-v1-auth-social-callback |
GET |
v1/auth/social/{authclient} |
Starts (and handles the provider’s callback for) the OAuth2 popup flow |
voyti/api-v1-auth-social-exchange |
POST |
v1/auth/social/exchange |
Trade the one-time code from the popup redirect for a real bearer token |
// config/params.php
return [
'yiirocks/voyti' => [
'api' => [
'social' => [
'redirectUrl' => 'https://spa.example.com/oauth-callback',
],
],
],
];''?code=... (success) or ?error=... in the query string. Required for the social-auth bridge to work; requests fail with 500 until it's set.Only the guest-login path is supported: connecting a social account to an
already-authenticated user, and completing a “pending” account link that needs manual registration
first, both require request context this stateless popup callback doesn’t have. Either case is
reported to the SPA as an error query parameter instead.