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 nameMethodPathPurpose
voyti/api-v1-auth-loginPOSTv1/auth/loginCredential login. Returns a bearer token, or a challenge_required body if 2FA is enabled for the account
voyti/api-v1-auth-registerPOSTv1/auth/registerSelf-registration
voyti/api-v1-auth-register-confirmGETv1/auth/register/confirm/{id}/{code}Confirm an emailed registration code
voyti/api-v1-auth-register-resendPOSTv1/auth/register/resendResend the confirmation email
voyti/api-v1-auth-password-reset-requestPOSTv1/auth/password-reset/requestRequest a password-recovery email
voyti/api-v1-auth-password-reset-confirmPOSTv1/auth/password-reset/confirmSet a new password from an emailed recovery code

Authenticated

Route nameMethodPathPurpose
voyti/api-v1-auth-logoutPOSTv1/auth/logoutRevoke the calling bearer token
voyti/api-v1-auth-me-showGETv1/auth/meThe current account’s profile
voyti/api-v1-auth-me-updatePATCHv1/auth/meUpdate username/password directly; an email change is routed through the confirmation flow instead of applied immediately
voyti/api-v1-auth-sessions-indexGETv1/auth/sessionsList the account’s active bearer tokens
voyti/api-v1-auth-sessions-terminateDELETEv1/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 nameMethodPathPurpose
voyti/api-v1-audit-log-indexGETv1/audit-logPaginated, filterable audit-log listing
voyti/api-v1-rbac-indexGETv1/rbac/{itemType}List roles or permissions
voyti/api-v1-rbac-createPOSTv1/rbac/{itemType}Create a role or permission
voyti/api-v1-rbac-updatePATCHv1/rbac/{itemType}/{name}Update name/description/rule/children
voyti/api-v1-rbac-deleteDELETEv1/rbac/{itemType}/{name}Delete a role or permission
voyti/api-v1-rbac-assignments-indexGETv1/rbac/{itemType}/{name}/assignmentsList users assigned to a role or permission
voyti/api-v1-rbac-assignments-updatePUTv1/rbac/{itemType}/{name}/assignmentsReplace 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 nameMethodPathPurpose
voyti/api-v1-auth-challenge-verifyPOSTv1/auth/challenge/verifyVerify 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 nameMethodPathPurpose
voyti/api-v1-2fa-statusGETv1/2faWhether 2FA is enabled, which method, available methods, and whether unused backup codes remain
voyti/api-v1-2fa-enablePOSTv1/2fa/enableEnable a code-based method; returns a fresh set of backup codes
voyti/api-v1-2fa-disablePOSTv1/2fa/disableDisable 2FA (re-authentication required)
voyti/api-v1-2fa-backup-codes-regeneratePOSTv1/2fa/backup-codes/regenerateInvalidate 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 nameMethodPathPurpose
voyti/api-v1-2fa-totp-setupGETv1/2fa/totp/setupQR code and secret, reusing the account’s existing pending secret if one exists
voyti/api-v1-2fa-totp-renewPOSTv1/2fa/totp/renewAlways issues a fresh QR code and secret
voyti/api-v1-2fa-email-send-codePOSTv1/2fa/email/send-codeEmails 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 nameMethodPathPurpose
voyti/api-v1-2fa-webauthn-register-startPOSTv1/2fa/webauthn/register/startBuilds the navigator.credentials.create() options and stashes the ceremony challenge
voyti/api-v1-2fa-webauthn-register-finishPOSTv1/2fa/webauthn/register/finishVerifies 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 nameMethodPathPurpose
voyti/api-v1-gdpr-exportGETv1/gdpr/exportData export as JSON
voyti/api-v1-gdpr-anonymizePOSTv1/gdpr/anonymizePassword-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 nameMethodPathPurpose
voyti/api-v1-auth-social-callbackGETv1/auth/social/{authclient}Starts (and handles the provider’s callback for) the OAuth2 popup flow
voyti/api-v1-auth-social-exchangePOSTv1/auth/social/exchangeTrade the one-time code from the popup redirect for a real bearer token

// 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.