Voyti

Voyti

User management, authentication & authorization

Stateless Client

A stateless REST API for any client that can hold a bearer token, a browser-based single-page application (SPA) foremost among them, 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.

Response messages are translatable. A consumer picks the language with a standard Accept-Language request header; voyti-api’s LocaleMiddleware matches it against the installed catalogs and falls back to ApiConfig::$defaultLocale if nothing matches.

Create an issue

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

RBAC mirrors core’s HTML admin screen: {itemType} is role or permission. Per-user assignment management is a separate sub-resource, not a property of the item itself: update diffs the submitted userIds against the current assignment list, removing and adding as needed, and rejects any ID that doesn’t match an existing user.

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
voyti/api-v1-rbac-assignments-index GET v1/rbac/{itemType}/{name}/assignments List users assigned to a role or permission
voyti/api-v1-rbac-assignments-update PUT v1/rbac/{itemType}/{name}/assignments Replace the full set of assigned users (diffs against the current assignment list)

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

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

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',
            ],
        ],
    ],
];
redirectUrl string
''
URL the OAuth2 popup redirects to after a social login attempt, with either ?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.