Voyti

Voyti

User management, authentication & authorization

Quick Start

1. Install

Optional packages to extend functionality:

Bot Protection
Google reCAPTCHA v2/v3 for registration and login forms
Two-Factor Authentication (TOTP)
Authenticator app enrollment with QR code provisioning
Social Authentication
OAuth2 login via Google, GitHub, Facebook, and more
return [
    'yiisoft/cookies' => [
        'secretKey' => $_ENV['COOKIES_SECRET'],
    ],
];

3. Run migrations

Voyti provides its migration path through config/params-console.php using the standard yiisoft/db-migration configuration keys. With yiisoft/db-migration enabled in your console app, run:

One migration creates the user, user_profile, user_social_account, user_token, user_sessions, user_backup_code, user_password_history, and user_audit_log tables with all columns (2FA, GDPR, password expiration, last login IP, etc.) included. config/params-console.php also registers yiisoft/rbac-db's own item/assignment migrations, so ./yii migrate:up creates the RBAC tables too - no separate step needed.

If the user table is still empty after these migrations run, a default admin account is seeded automatically: username admin, email admin@example.com, and a random 20-character password printed to the console - copy it immediately, it isn't stored anywhere else. The account is assigned the administrator role, which is granted the administratorPermissionName permission (voyti-admin by default) needed to reach the admin dashboard. Change this password immediately after first login. If the user table already has rows (e.g. re-running migrations on an existing database), seeding is skipped entirely.

4. Register routes

Routes are not auto-registered - you must add them to your router configuration.

Pull the voyti-routes config group into your router definition. The example below mounts them under a /user/ prefix as their own group, alongside your app's own routes - change the prefix as needed. It also shows the optional REST API group, mounted as its own sibling group; skip it if you don't need the API (see below):

use Yiisoft\Config\Config;
use Yiisoft\Definitions\DynamicReference;
use Yiisoft\Router\Group;
use Yiisoft\Router\RouteCollection;
use Yiisoft\Router\RouteCollectionInterface;
use Yiisoft\Router\RouteCollector;
use Yiisoft\Session\SessionMiddleware;
use YiiRocks\Voyti\Middleware\VoytiMiddleware;

/** @var Config $config */

return [
    RouteCollectionInterface::class => [
        'class' => RouteCollection::class,
        '__construct()' => [
            'collector' => DynamicReference::to(
                static fn() => (new RouteCollector())
                    ->addRoute(
                        Group::create('/')
                            ->middleware(
                                SessionMiddleware::class,                  # required for site-wide session support
                                VoytiMiddleware::class,                    # Site-wide enforcement
                            )
                            ->routes(...$config->get('routes')),           # Your routes
                        Group::create('/user/')                            # Voyti web URL prefix
                            ->routes(...$config->get('voyti-routes')),     # Voyti web routes
                        Group::create('/user/api/')                        # REST API URL prefix - optional
                            ->routes(...$config->get('voyti-routes-api')), # delete this group when unneeded
                    )
            ),
        ],
    ],
];

voyti-routes and voyti-routes-api each already wrap themselves with their own required middleware (see config/routes.php and config/routes-api.php), so neither group above repeats any of it, and adding VoytiMiddleware to your own group only extends that same coverage to your app's pages.

The REST API routes are entirely optional and not part of voyti-routes - they live in their own voyti-routes-api config group, which nothing else depends on. If you don't need the API, skip pulling that group in and none of it is registered; there's no config flag to enable it or prefix option to set, the group's own registration is the switch. When you do want it, mount it wherever you like - the /api/ sibling group shown above is just one option.

5. Configure the form theme

Voyti's forms (login, registration, profile, etc.) and its button-styled links (e.g. "Create user") render through yiisoft/form's ThemeContainer. Voyti ships no theme of its own, so until your app configures one, every field and button renders with no CSS classes at all - no form-control, no btn, nothing.

Set a theme in config/params.php. yiisoft/form ships ready-made Bootstrap 5 configs you can use as-is:

use Yiisoft\Form\Theme\ThemePath;
use Yiisoft\FormModel\ValidationRulesEnricher;

return [
    'yiisoft/form' => [
        'themes' => [
            'default' => [
                ...require ThemePath::BOOTSTRAP5_VERTICAL,
                'enrichFromValidationRules' => true,
                'validationRulesEnricher' => new ValidationRulesEnricher(),
            ],
        ],
        'defaultTheme' => 'default',
    ],
];

Swap in ThemePath::BOOTSTRAP5_HORIZONTAL for a horizontal label/input layout, or write your own array of Theme::__construct() options if you're not using Bootstrap. The key doesn't have to be named bootstrap5 - ThemeContainer resolves a single named config per Theme instance, so enrichFromValidationRules and validationRulesEnricher() below have to live in whichever config your defaultTheme points at, not in a separate Bootstrap-only entry. Naming it default (matching yiisoft/form's own config/params.php stub) keeps that clear.

enrichFromValidationRules and validationRulesEnricher are worth setting alongside the theme: they translate the yiisoft/validator rules already on your form models (Required, Length, Regex, etc.) into matching HTML5 input attributes (required, minlength/ maxlength, pattern, and so on), giving you client-side validation for free instead of relying on a server round-trip alone.

6. Done

DI bindings, event listeners, and console commands are auto-registered via the Yii3 config plugin. No manual wiring needed.