С ИИ написал авторизацию. Проверить пока не могу. Нужно в VK ID бизнес зарегистрироваться. Бизнесменом быть необязательно, сказали в поддержке, просто не будет расширенных функций.
Было бы неплохо, если бы нашелся программист, который бы подкорректировал код, если там есть какие-то проблемы.
<!-- Place this code in your Joomla login template -->
<div id="vkid-auth-container">
<script src="https://unpkg.com/@vkid/sdk@<3.0.0/dist-sdk/umd/index.js"></script>
<script type="text/javascript">
if ('VKIDSDK' in window) {
const VKID = window.VKIDSDK;
// CONFIGURATION FOR SITE.COM
VKID.Config.init({
app: 11111111, // Client ID for site.com
redirectUrl: 'https://site.com',
responseMode: VKID.ConfigResponseMode.Callback,
source: VKID.ConfigSource.LOWCODE,
scope: '', // No email requested
});
const oneTap = new VKID.OneTap();
oneTap.render({
container: document.getElementById('vkid-auth-container'),
showAlternativeLogin: true, // Shows additional OK and Mail.ru buttons
oauthList: ['ok_ru', 'mail_ru'],
lang: 3 // English interface, Russian interface = 0
})
.on(VKID.WidgetEvents.ERROR, function(error) {
console.error('VK ID Error:', error);
alert('Authorization error. Please try again.');
})
.on(VKID.OneTapInternalEvents.LOGIN_SUCCESS, function(payload) {
const code = payload.code;
const deviceId = payload.device_id;
// Redirect to Joomla backend for processing
window.location.href = 'https://site.com/index.php?option=com_ajax&plugin=vkid&format=raw&task=auth&code=' +
encodeURIComponent(code) + '&device_id=' + encodeURIComponent(deviceId);
});
} else {
console.error('VK ID SDK failed to load');
}
</script>
</div>
Далее плагин со структурой:
plugins/authentication/vkid/
├── services/
│ └── provider.php
├── src/
│ └── Extension/
│ └── Vkid.php
├── language/
│ └── en-GB/
│ └── en-GB.plg_authentication_vkid.ini
└── vkid.xml
provider.php
<?php
defined('_JEXEC') or die;
use Joomla\CMS\Extension\PluginInterface;
use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\DI\Container;
use Joomla\DI\ServiceProviderInterface;
use Joomla\Event\DispatcherInterface;
use Joomla\Plugin\Authentication\Vkid\Extension\Vkid;
return new class implements ServiceProviderInterface
{
public function register(Container $container)
{
$container->set(
PluginInterface::class,
function (Container $container) {
$plugin = new Vkid(
$container->get(DispatcherInterface::class),
(array) PluginHelper::getPlugin('authentication', 'vkid')
);
$plugin->setApplication(Factory::getApplication());
return $plugin;
}
);
}
};
Vkid.php
<?php
namespace Joomla\Plugin\Authentication\Vkid\Extension;
defined('_JEXEC') or die;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Factory;
use Joomla\CMS\User\User;
use Joomla\CMS\User\UserHelper;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Http\HttpFactory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Log\Log;
use Joomla\Registry\Registry;
/**
* VK ID Authentication Plugin for Joomla 5/6
*/
class Vkid extends CMSPlugin
{
/**
* Handle AJAX requests for VK ID authentication
*/
public function onAjaxVkid()
{
$app = Factory::getApplication();
$input = $app->getInput();
$this->log('Starting VK ID authentication process');
if ($input->getString('task')!== 'auth') {
return json_encode(['success' => false, 'error' => 'Invalid task']);
}
$code = $input->getString('code');
$device_id = $input->getString('device_id');
if (empty($code)) {
$this->log('No authorization code provided');
return json_encode(['success' => false, 'error' => 'No authorization code']);
}
try {
// 1. Exchange code for token (VK ID API)
$tokenData = $this->exchangeCodeForToken($code);
if (!isset($tokenData['access_token'])) {
throw new \Exception('Failed to get access token from VK ID');
}
// 2. Get user info
$userInfo = $this->getUserInfo($tokenData['access_token']);
if (!$userInfo) {
throw new \Exception('Failed to get user information from VK');
}
// 3. Create or update user
$userData = $this->createOrUpdateUser($userInfo);
// 4. Login user
$this->loginUser($userData);
$this->log('VK ID authentication completed successfully for user ID: ' . $userData['id']);
// Redirect to home page
return json_encode(['success' => true, 'redirect' => Uri::base()]);
} catch (\Exception $e) {
$this->log('Authentication failed: ' . $e->getMessage(), Log::ERROR);
return json_encode(['success' => false, 'error' => $e->getMessage()]);
}
}
/**
* Exchange authorization code for access token (VK ID API)
*/
private function exchangeCodeForToken($code)
{
$clientId = $this->params->get('client_id', '');
$clientSecret = $this->params->get('client_secret', '');
$siteDomain = $this->params->get('site_domain', '');
if (empty($clientId) || empty($clientSecret) || empty($siteDomain)) {
throw new \Exception('VK ID plugin not configured. Check Client ID, Secret and Domain.');
}
$redirectUri = 'https://' . $siteDomain;
// VK ID SPECIFIC ENDPOINT - CORRECT
$url = '[url=https://id.vk.com/oauth2/auth';]https://id.vk.com/oauth2/auth';[/url]
$params = [
'grant_type' => 'authorization_code',
'client_id' => $clientId,
'client_secret' => $clientSecret,
'redirect_uri' => $redirectUri,
'code' => $code
];
$http = HttpFactory::getHttp();
// MUST BE POST REQUEST
$response = $http->post($url, $params);
if ($response->code !== 200) {
throw new \Exception('VK ID API error: HTTP ' . $response->code . ' - ' . $response->body);
}
$data = json_decode($response->body, true);
if (isset($data['error'])) {
throw new \Exception('VK ID error: ' . ($data['error_description'] ?? $data['error']));
}
$this->log('Token exchange successful');
return $data;
}
/**
* Get user information from VK API
*/
private function getUserInfo($accessToken)
{
// Use .ru domain for API (Russia)
$url = '[url=https://api.vk.ru/method/users.get?]https://api.vk.ru/method/users.get?[/url]' . http_build_query([
'fields' => 'first_name,last_name',
'access_token' => $accessToken,
'v' => '5.131'
]);
$http = HttpFactory::getHttp();
$response = $http->get($url);
$data = json_decode($response->body, true);
if (isset($data['error'])) {
throw new \Exception('VK API error: ' . $data['error']['error_msg']);
}
return $data['response'][0] ?? null;
}
/**
* Create or update user
*/
private function createOrUpdateUser($userInfo)
{
$vkId = $userInfo['id'];
$username = 'vk_' . $vkId;
$email = $username . '@' . $this->params->get('site_domain', 'vk.auth');
$name = trim(($userInfo['first_name'] ?? ''). ' ' . ($userInfo['last_name'] ?? ''));
// Check if user exists
$userId = UserHelper::getUserId($username);
if ($userId) {
// Update existing user (only name)
$user = Factory::getUser($userId);
$user->set('name', $name);
if (!$user->save()) {
throw new \Exception('Failed to update user: ' . $user->getError());
}
// Get the password for login
$user = Factory::getUser($userId);
$password = $user->password_clear; // Will be empty after first save
// If password_clear is empty (it will be), we need to generate a temporary one
if (empty($password)) {
$password = UserHelper::genRandomPassword(16);
$user->set('password', $password);
$user->save();
}
} else {
// Create new user
$user = new User();
$password = UserHelper::genRandomPassword(16);
$user->set('name', $name);
$user->set('username', $username);
$user->set('email', $email);
$user->set('password', $password);
$user->set('block', 0);
$user->set('groups', [2]); // Registered
if (!$user->save()) {
throw new \Exception('Failed to create user: ' . $user->getError());
}
$userId = $user->id;
$this->log('New user created: ' . $username . ' (ID: ' . $userId . ')');
}
return [
'id' => $userId,
'username' => $username,
'password' => $password
];
}
/**
* Login user
*/
private function loginUser($userData)
{
$app = Factory::getApplication();
$credentials = [
'username' => $userData['username'],
'password' => $userData['password']
];
$options = [
'remember' => false,
'silent' => true,
'action' => 'core.login.site'
];
$result = $app->login($credentials, $options);
if ($result !== true) {
throw new \Exception('Login failed for user: ' . $userData['username']);
}
$this->log('User logged in successfully: ' . $userData['username']);
}
/**
* Logging
*/
private function log($message, $type = Log::INFO)
{
if ($this->params->get('enable_logging', 0)) {
Log::add('VK ID: ' . $message, $type, 'plg_authentication_vkid');
}
}
}
en-GB.plg_authentication_vkid.ini
PLG_AUTHENTICATION_VKID="VK ID Authentication"
PLG_AUTHENTICATION_VKID_XML_DESCRIPTION="VK ID authentication plugin for Joomla"
; PLG_AUTHENTICATION_VKID_ERROR_GENERIC="Authentication error"
; PLG_AUTHENTICATION_VKID_ERROR_CONFIG="Plugin not configured properly"
; PLG_AUTHENTICATION_VKID_ERROR_TOKEN="Failed to get access token"
; PLG_AUTHENTICATION_VKID_ERROR_USERINFO="Failed to get user information"
; PLG_AUTHENTICATION_VKID_ERROR_CREATE_USER="Failed to create user account"
vkid.xml
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="authentication" method="upgrade">
<name>Authentication - VK ID</name>
<version>1.0.0</version>
<creationDate>2025-01-01</creationDate>
<author>Free</author>
<description>VK ID Authentication for Joomla 5</description>
<namespace>Joomla\Plugin\Authentication\Vkid</namespace>
<files>
<folder plugin="vkid">services</folder>
<folder>src</folder>
<folder>language</folder>
</files>
<services>
<service provider="Joomla\Plugin\Authentication\Vkid\ServiceProvider">
<arg key="authentication" type="service" id="authentication"/>
</service>
</services>
<config>
<fields name="params">
<fieldset name="basic">
<field
name="client_id"
type="text"
label="Client ID"
required="true"
description="Client ID из кабинета VK ID"/>
<field
name="client_secret"
type="text"
label="Client Secret"
required="true"
description="Секретный ключ из кабинета VK ID"/>
<field
name="site_domain"
type="text"
label="Site Domain"
required="true"
description="Без http/https, например: site.com"/>
<field
name="enable_logging"
type="radio"
label="Enable Debug Logging"
default="0"
class="btn-group btn-group-yesno">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
</fieldset>
</fields>
</config>
</extension>