tigusigalpa/vtb-id-php

VTB ID OAuth2 integration package for Laravel

Installs: 0

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/tigusigalpa/vtb-id-php

v1.0.0 2025-12-24 05:25 UTC

This package is auto-updated.

Last update: 2025-12-24 05:31:13 UTC


README

VTBID PHP

Latest Version License

PHP/Laravel пакет для интеграции с VTB ID (OAuth2 авторизация через аккаунт в ВТБ Банке).

🌐 Язык: Русский | English

Содержание

Требования

  • PHP 8.0 или выше
  • Guzzle HTTP Client 7.x
  • Laravel 8.x, 9.x, 10.x, 11.x или 12.x (опционально, для Laravel интеграции)

Установка

Через Composer (рекомендуется)

composer require tigusigalpa/vtb-id-php

Публикация конфигурации

php artisan vendor:publish --tag=vtbid-config

Локальная установка (для разработки)

Добавьте репозиторий в composer.json:

{
    "repositories": [
        {
            "type": "path",
            "url": "./packages/vtb-id-php"
        }
    ],
    "require": {
        "tigusigalpa/vtb-id-php": "@dev"
    }
}

Затем установите:

composer require tigusigalpa/vtb-id-php:@dev

Быстрый старт

Для Laravel

1. Установка (1 минута)

composer require tigusigalpa/vtb-id-php
php artisan vendor:publish --tag=vtbid-config

2. Конфигурация (2 минуты)

Добавьте в .env:

VTBID_CLIENT_ID=ваш_client_id_от_vtb
VTBID_CLIENT_SECRET=ваш_client_secret_от_vtb
VTBID_REDIRECT_URI=https://ваш-сайт.ru/auth/vtb/callback

3. Создайте маршруты (2 минуты)

В routes/web.php:

// Успешная авторизация
Route::get('/auth/vtb/success', function () {
    $user = session('vtbid_user');
    
    // Здесь создайте/обновите пользователя в БД
    // и авторизуйте его в системе
    
    dd($user); // Для тестирования
})->name('vtbid.success');

// Ошибка авторизации
Route::get('/auth/vtb/error', function () {
    $error = session('error');
    return redirect('/login')->with('error', $error);
})->name('vtbid.error');

4. Добавьте кнопку входа

В любом blade-шаблоне:

<a href="{{ route('vtbid.redirect') }}" class="btn btn-primary">
    Войти через VTB ID
</a>

Для чистого PHP

1. Установка

composer require tigusigalpa/vtb-id-php

2. Создайте файл конфигурации

// config.php
return [
    'client_id' => 'your_client_id',
    'client_secret' => 'your_client_secret',
    'redirect_uri' => 'https://your-domain.com/callback.php',
];

3. Страница авторизации (login.php)

<?php
require_once 'vendor/autoload.php';

use Tigusigalpa\VTBID\VTBIDClient;

session_start();

$config = require 'config.php';
$client = new VTBIDClient($config);

$state = $client->generateState();
$_SESSION['vtbid_state'] = $state;

$authUrl = $client->getAuthorizationUrl($state);

header('Location: ' . $authUrl);
exit;

4. Обработка callback (callback.php)

<?php
require_once 'vendor/autoload.php';

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

session_start();

$config = require 'config.php';
$client = new VTBIDClient($config);

$code = $_GET['code'] ?? null;
$state = $_GET['state'] ?? null;

// Проверка state
if (!$code || !isset($_SESSION['vtbid_state']) || $_SESSION['vtbid_state'] !== $state) {
    die('Invalid request');
}

unset($_SESSION['vtbid_state']);

try {
    // Получение токена
    $tokenData = $client->getAccessToken($code);
    
    // Получение данных пользователя
    $userData = $client->getUserData($tokenData['access_token']);
    
    // Сохранение в сессию
    $_SESSION['vtbid_access_token'] = $tokenData['access_token'];
    $_SESSION['vtbid_refresh_token'] = $tokenData['refresh_token'] ?? null;
    $_SESSION['vtbid_user'] = $userData->toArray();
    
    // Перенаправление на главную
    header('Location: /dashboard.php');
    exit;
    
} catch (VTBIDException $e) {
    die('Ошибка авторизации: ' . $e->getMessage());
}

5. Использование данных пользователя (dashboard.php)

<?php
session_start();

if (!isset($_SESSION['vtbid_user'])) {
    header('Location: /login.php');
    exit;
}

$user = $_SESSION['vtbid_user'];
?>

<!DOCTYPE html>
<html>
<head>
    <title>Dashboard</title>
</head>
<body>
    <h1>Добро пожаловать, <?= htmlspecialchars($user['name']) ?>!</h1>
    <p>Email: <?= htmlspecialchars($user['email']) ?></p>
    <p>Телефон: <?= htmlspecialchars($user['mainMobilePhone']) ?></p>
    <a href="/logout.php">Выйти</a>
</body>
</html>

Готово! 🎉

Теперь пользователи могут авторизоваться через VTB ID.

Конфигурация

Переменные окружения

Добавьте в .env:

VTBID_CLIENT_ID=your_client_id
VTBID_CLIENT_SECRET=your_client_secret
VTBID_REDIRECT_URI=https://your-domain.com/auth/vtb/callback

# Опциональные параметры
VTBID_BASE_URL=https://id.vtb.ru
VTBID_USER_DATA_URL=https://gost-id.vtb.ru
VTBID_SCOPE="openid name surname patronymic gender mainMobilePhone email"
VTBID_RESPONSE_TYPE=code

# Настройки маршрутов
VTBID_ROUTES_ENABLED=true
VTBID_ROUTES_PREFIX=auth/vtb

Получение учетных данных VTB ID

  1. Зарегистрируйтесь на портале VTB ID для разработчиков
  2. Создайте новое приложение
  3. Укажите Redirect URI: https://your-domain.com/auth/vtb/callback
  4. Выберите необходимые scope
  5. Получите client_id и client_secret

Доступные Scope

openid              - Обязательный, базовая авторизация
name                - Имя
surname             - Фамилия
patronymic          - Отчество
gender              - Пол
birthdate           - Дата рождения
mainMobilePhone     - Основной телефон
email               - Email
inn                 - ИНН
snils               - СНИЛС

Использование

Базовое использование с встроенными маршрутами

Пакет автоматически регистрирует следующие маршруты:

  • GET /auth/vtb/redirect - Перенаправление на страницу авторизации VTB ID
  • GET /auth/vtb/callback - Обработка callback от VTB ID

Для начала авторизации просто перенаправьте пользователя на:

return redirect()->route('vtbid.redirect');

Использование VTBIDClient напрямую

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

// Через Dependency Injection
public function __construct(protected VTBIDClient $vtbid)
{
}

// Или через Facade
use Tigusigalpa\VTBID\Facades\VTBID;

// Получение URL для авторизации
$state = VTBID::generateState();
session(['vtbid_state' => $state]);
$authUrl = VTBID::getAuthorizationUrl($state);

return redirect($authUrl);

// Обработка callback
try {
    $tokenData = VTBID::getAccessToken($code);
    // $tokenData содержит: access_token, refresh_token, expires_in, token_type
    
    $userData = VTBID::getUserData($tokenData['access_token']);
    
    // Доступ к данным пользователя
    echo $userData->name;
    echo $userData->surname;
    echo $userData->email;
    echo $userData->getFullName(); // Фамилия Имя Отчество
    
} catch (VTBIDException $e) {
    Log::error('VTB ID Error: ' . $e->getMessage());
}

Обновление токена

try {
    $refreshToken = session('vtbid_refresh_token');
    $newTokenData = VTBID::refreshAccessToken($refreshToken);
    
    session([
        'vtbid_access_token' => $newTokenData['access_token'],
        'vtbid_refresh_token' => $newTokenData['refresh_token'] ?? null,
    ]);
} catch (VTBIDException $e) {
    // Токен истек или невалиден
}

Отзыв токена

try {
    $accessToken = session('vtbid_access_token');
    VTBID::revokeToken($accessToken, 'access_token');
    
    session()->forget(['vtbid_access_token', 'vtbid_refresh_token', 'vtbid_user']);
} catch (VTBIDException $e) {
    Log::error('Failed to revoke token: ' . $e->getMessage());
}

Helper функции

// Проверка авторизации
if (vtbid_authenticated()) {
    $user = vtbid_user();
    $token = vtbid_access_token();
}

Данные пользователя

Объект VTBIDUser содержит следующие поля:

  • sub - Уникальный идентификатор пользователя
  • name - Имя
  • surname - Фамилия
  • patronymic - Отчество
  • gender - Пол (male/female)
  • birthdate - Дата рождения
  • mainMobilePhone - Основной мобильный телефон
  • email - Email
  • emailVerified - Email подтвержден (boolean)
  • phoneNumberVerified - Телефон подтвержден (boolean)
  • inn - ИНН
  • snils - СНИЛС

Методы VTBIDUser

$userData->getFullName(); // Возвращает "Фамилия Имя Отчество"
$userData->toArray(); // Преобразует в массив
$userData->getRawData(); // Возвращает исходные данные от API

Интеграция с Laravel Auth

Обновление миграции пользователей

php artisan make:migration add_vtb_id_fields_to_users_table
public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('vtb_id')->nullable()->unique()->after('id');
        $table->string('surname')->nullable()->after('name');
        $table->string('patronymic')->nullable()->after('surname');
        $table->string('phone')->nullable()->after('email');
    });
}
php artisan migrate

Обновление модели User

protected $fillable = [
    'name',
    'surname',
    'patronymic',
    'email',
    'phone',
    'vtb_id',
    'password',
];

Пример контроллера

use App\Models\User;
use Illuminate\Support\Facades\Auth;

Route::get('/auth/vtb/success', function () {
    $vtbUser = session('vtbid_user');
    
    $user = User::updateOrCreate(
        ['vtb_id' => $vtbUser['sub']],
        [
            'name' => $vtbUser['name'],
            'surname' => $vtbUser['surname'],
            'patronymic' => $vtbUser['patronymic'],
            'email' => $vtbUser['email'],
            'phone' => $vtbUser['mainMobilePhone'],
        ]
    );
    
    Auth::login($user, true);
    
    return redirect('/dashboard');
})->name('vtbid.success');

API Reference

VTBIDClient

Constructor

public function __construct(array $config = [])

Параметры:

  • client_id (string) - ID приложения VTB ID
  • client_secret (string) - Секретный ключ приложения
  • redirect_uri (string) - URL для callback
  • base_url (string, optional) - Базовый URL API
  • user_data_url (string, optional) - URL для данных пользователя
  • scope (string, optional) - Запрашиваемые разрешения
  • response_type (string, optional) - Тип ответа

getAuthorizationUrl()

public function getAuthorizationUrl(?string $state = null): string

Генерирует URL для перенаправления пользователя на страницу авторизации VTB ID.

generateState()

public function generateState(): string

Генерирует случайную строку для параметра state (CSRF защита).

getAccessToken()

public function getAccessToken(string $code): array

Обменивает код авторизации на access token.

Возвращает:

[
    'access_token' => '...',
    'refresh_token' => '...',
    'expires_in' => 3600,
    'token_type' => 'Bearer'
]

refreshAccessToken()

public function refreshAccessToken(string $refreshToken): array

Обновляет access token используя refresh token.

getUserData()

public function getUserData(string $accessToken): VTBIDUser

Получает данные пользователя используя access token.

revokeToken()

public function revokeToken(string $token, string $tokenTypeHint = 'access_token'): bool

Отзывает (аннулирует) токен.

Facade: VTBID

use Tigusigalpa\VTBID\Facades\VTBID;

$authUrl = VTBID::getAuthorizationUrl();
$state = VTBID::generateState();
$tokenData = VTBID::getAccessToken($code);
$userData = VTBID::getUserData($accessToken);
$newTokenData = VTBID::refreshAccessToken($refreshToken);
VTBID::revokeToken($token);

Middleware

VTBIDAuthenticated

Регистрация в app/Http/Kernel.php:

protected $routeMiddleware = [
    'vtbid.auth' => \Tigusigalpa\VTBID\Middleware\VTBIDAuthenticated::class,
];

Использование:

Route::middleware(['vtbid.auth'])->group(function () {
    Route::get('/profile', function () {
        return view('profile', ['user' => vtbid_user()]);
    });
});

Примеры использования

Использование с чистым PHP

Полный пример приложения

Структура проекта:

/
├── vendor/
├── config.php
├── login.php
├── callback.php
├── dashboard.php
├── logout.php
└── composer.json

logout.php:

<?php
require_once 'vendor/autoload.php';

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

session_start();

$config = require 'config.php';
$client = new VTBIDClient($config);

// Отзыв токена
if (isset($_SESSION['vtbid_access_token'])) {
    try {
        $client->revokeToken($_SESSION['vtbid_access_token']);
    } catch (VTBIDException $e) {
        // Логирование ошибки
        error_log('Failed to revoke token: ' . $e->getMessage());
    }
}

// Очистка сессии
session_destroy();

header('Location: /login.php');
exit;

Обновление токена:

<?php
require_once 'vendor/autoload.php';

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

session_start();

$config = require 'config.php';
$client = new VTBIDClient($config);

if (isset($_SESSION['vtbid_refresh_token'])) {
    try {
        $newTokenData = $client->refreshAccessToken($_SESSION['vtbid_refresh_token']);
        
        $_SESSION['vtbid_access_token'] = $newTokenData['access_token'];
        $_SESSION['vtbid_refresh_token'] = $newTokenData['refresh_token'] ?? $_SESSION['vtbid_refresh_token'];
        
        echo "Токен обновлен успешно!";
    } catch (VTBIDException $e) {
        echo "Ошибка обновления токена: " . $e->getMessage();
        header('Location: /login.php');
        exit;
    }
}

Интеграция с базой данных:

<?php
// callback.php с сохранением в БД
require_once 'vendor/autoload.php';

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

session_start();

$config = require 'config.php';
$client = new VTBIDClient($config);

$code = $_GET['code'] ?? null;
$state = $_GET['state'] ?? null;

if (!$code || !isset($_SESSION['vtbid_state']) || $_SESSION['vtbid_state'] !== $state) {
    die('Invalid request');
}

unset($_SESSION['vtbid_state']);

try {
    $tokenData = $client->getAccessToken($code);
    $userData = $client->getUserData($tokenData['access_token']);
    
    // Подключение к БД
    $pdo = new PDO('mysql:host=localhost;dbname=myapp', 'username', 'password');
    
    // Поиск или создание пользователя
    $stmt = $pdo->prepare("
        INSERT INTO users (vtb_id, name, surname, patronymic, email, phone, created_at, updated_at)
        VALUES (:vtb_id, :name, :surname, :patronymic, :email, :phone, NOW(), NOW())
        ON DUPLICATE KEY UPDATE
            name = :name,
            surname = :surname,
            patronymic = :patronymic,
            email = :email,
            phone = :phone,
            updated_at = NOW()
    ");
    
    $stmt->execute([
        'vtb_id' => $userData->sub,
        'name' => $userData->name,
        'surname' => $userData->surname,
        'patronymic' => $userData->patronymic,
        'email' => $userData->email,
        'phone' => $userData->mainMobilePhone,
    ]);
    
    $userId = $pdo->lastInsertId() ?: $pdo->query("SELECT id FROM users WHERE vtb_id = '{$userData->sub}'")->fetchColumn();
    
    // Сохранение в сессию
    $_SESSION['user_id'] = $userId;
    $_SESSION['vtbid_access_token'] = $tokenData['access_token'];
    $_SESSION['vtbid_user'] = $userData->toArray();
    
    header('Location: /dashboard.php');
    exit;
    
} catch (VTBIDException $e) {
    die('Ошибка авторизации: ' . $e->getMessage());
} catch (PDOException $e) {
    die('Ошибка БД: ' . $e->getMessage());
}

Кастомные маршруты (Laravel)

Отключите встроенные маршруты в .env:

VTBID_ROUTES_ENABLED=false

Создайте свои маршруты:

use Tigusigalpa\VTBID\VTBIDClient;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

Route::get('/custom/vtb/login', function (VTBIDClient $vtbid) {
    $state = $vtbid->generateState();
    session(['vtbid_state' => $state]);
    return redirect($vtbid->getAuthorizationUrl($state));
});

Route::get('/custom/vtb/callback', function (Request $request, VTBIDClient $vtbid) {
    $code = $request->input('code');
    $state = $request->input('state');
    
    if (!$code || session('vtbid_state') !== $state) {
        return redirect()->route('login')->with('error', 'Invalid request');
    }
    
    try {
        $tokenData = $vtbid->getAccessToken($code);
        $userData = $vtbid->getUserData($tokenData['access_token']);
        
        // Ваша логика
        
        return redirect()->route('dashboard');
    } catch (VTBIDException $e) {
        return redirect()->route('login')->with('error', $e->getMessage());
    }
});

Обработка ошибок

Глобальный обработчик в app/Exceptions/Handler.php:

use Tigusigalpa\VTBID\Exceptions\VTBIDException;

public function register()
{
    $this->renderable(function (VTBIDException $e, $request) {
        \Log::error('VTB ID Exception', [
            'message' => $e->getMessage(),
            'code' => $e->getCode(),
        ]);

        if ($request->expectsJson()) {
            return response()->json([
                'error' => 'vtb_id_error',
                'message' => $e->getMessage(),
            ], 400);
        }

        return redirect()->route('login')
            ->with('error', 'Ошибка VTB ID: ' . $e->getMessage());
    });
}

Автоматическое обновление токена

Middleware для автоматического обновления:

namespace App\Http\Middleware;

use Closure;
use Tigusigalpa\VTBID\Facades\VTBID;
use Tigusigalpa\VTBID\Exceptions\VTBIDException;

class RefreshVTBIDToken
{
    public function handle(Request $request, Closure $next)
    {
        if (!session()->has('vtbid_access_token')) {
            return redirect()->route('vtbid.redirect');
        }

        $expiresAt = session('vtbid_token_expires_at');
        
        if ($expiresAt && now()->addMinutes(5)->greaterThan($expiresAt)) {
            $refreshToken = session('vtbid_refresh_token');
            
            if ($refreshToken) {
                try {
                    $newTokenData = VTBID::refreshAccessToken($refreshToken);
                    
                    session([
                        'vtbid_access_token' => $newTokenData['access_token'],
                        'vtbid_refresh_token' => $newTokenData['refresh_token'] ?? $refreshToken,
                        'vtbid_token_expires_at' => now()->addSeconds($newTokenData['expires_in'] ?? 3600),
                    ]);
                } catch (VTBIDException $e) {
                    session()->forget(['vtbid_access_token', 'vtbid_refresh_token']);
                    return redirect()->route('vtbid.redirect');
                }
            }
        }

        return $next($request);
    }
}

Безопасность

Обязательные меры

  1. HTTPS обязателен - VTB ID не работает с HTTP
  2. Защита client_secret - никогда не коммитьте в Git
  3. Проверка state - защита от CSRF (уже реализована в пакете)
  4. Валидация redirect_uri - должен точно совпадать с зарегистрированным

Рекомендации

// Проверяйте email_verified
if (!$vtbUser['email_verified']) {
    return redirect()->back()
        ->with('warning', 'Email не подтвержден');
}

// Логируйте попытки авторизации
\Log::info('VTB ID login attempt', [
    'vtb_id' => $vtbUser['sub'],
    'email' => $vtbUser['email'],
]);

Настройка для Production

HTTPS обязателен

APP_URL=https://your-domain.com
VTBID_REDIRECT_URI=https://your-domain.com/auth/vtb/callback

Безопасность сессий

В config/session.php:

'secure' => env('SESSION_SECURE_COOKIE', true),
'http_only' => true,
'same_site' => 'lax',

Кэширование конфигурации

php artisan config:cache
php artisan route:cache

Тестирование

composer test

Проверка установки

php artisan tinker
>>> config('vtbid.client_id')
=> "your_client_id"

>>> app(Tigusigalpa\VTBID\VTBIDClient::class)
=> Tigusigalpa\VTBID\VTBIDClient {#...}

>>> route('vtbid.redirect')
=> "http://your-domain.test/auth/vtb/redirect"

Проверка маршрутов

php artisan route:list | grep vtb

Устранение проблем

"Class VTBIDServiceProvider not found"

composer dump-autoload
php artisan config:clear
php artisan cache:clear

"Route [vtbid.success] not defined"

Создайте маршрут vtbid.success в routes/web.php

"invalid_client"

Проверьте VTBID_CLIENT_ID и VTBID_CLIENT_SECRET в .env

"redirect_uri_mismatch"

Убедитесь, что VTBID_REDIRECT_URI точно совпадает с зарегистрированным в VTB ID

Документация VTB ID

Endpoints

  • Authorization URL: https://id.vtb.ru/
  • Token URL: https://id.vtb.ru/oauth2/token
  • User Data URL: https://gost-id.vtb.ru/oauth2/me
  • Revoke Token URL: https://id.vtb.ru/oauth2/revoke

Лицензия

MIT License. Подробности в LICENSE.md.

Автор

Igor Sazonov

Поддержка

Если у вас возникли проблемы или вопросы, создайте issue на GitHub.

Made with ❤️ for Laravel community