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
Requires
- php: ^8.0|^8.1|^8.2|^8.3
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^8.0|^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.0|^10.0
README
PHP/Laravel пакет для интеграции с VTB ID (OAuth2 авторизация через аккаунт в ВТБ Банке).
🌐 Язык: Русский | English
Содержание
- Требования
- Установка
- Быстрый старт
- Конфигурация
- Использование
- API Reference
- Примеры использования
- Безопасность
- Тестирование
Требования
- 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
- Зарегистрируйтесь на портале VTB ID для разработчиков
- Создайте новое приложение
- Укажите Redirect URI:
https://your-domain.com/auth/vtb/callback - Выберите необходимые scope
- Получите
client_idиclient_secret
Доступные Scope
openid - Обязательный, базовая авторизация
name - Имя
surname - Фамилия
patronymic - Отчество
gender - Пол
birthdate - Дата рождения
mainMobilePhone - Основной телефон
email - Email
inn - ИНН
snils - СНИЛС
Использование
Базовое использование с встроенными маршрутами
Пакет автоматически регистрирует следующие маршруты:
GET /auth/vtb/redirect- Перенаправление на страницу авторизации VTB IDGET /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- EmailemailVerified- 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 IDclient_secret(string) - Секретный ключ приложенияredirect_uri(string) - URL для callbackbase_url(string, optional) - Базовый URL APIuser_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); } }
Безопасность
Обязательные меры
- HTTPS обязателен - VTB ID не работает с HTTP
- Защита client_secret - никогда не коммитьте в Git
- Проверка state - защита от CSRF (уже реализована в пакете)
- Валидация 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
- Email: sovletig@gmail.com
- GitHub: @tigusigalpa
Поддержка
Если у вас возникли проблемы или вопросы, создайте issue на GitHub.
Made with ❤️ for Laravel community