gam6itko / oauth2-vk-id
VK ID (OAuth 2.1, id.vk.ru) provider for league/oauth2-client — PKCE, device_id, no superglobals (RoadRunner/Swoole-safe).
Requires
- php: ^8.1
- ext-json: *
- league/oauth2-client: ^2.7
Requires (Dev)
- phpunit/phpunit: ^10.5 || ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
VK ID (OAuth 2.1, id.vk.ru) provider for The PHP League OAuth2 Client.
See the official VK ID web auth flow docs for the underlying protocol.
Unlike the other oauth2-vkontakte packages on Packagist:
- it targets the new VK ID endpoints (
id.vk.ru), not the deprecatedoauth.vk.com/api.vk.comOAuth 2.0; - it keeps no hidden state — it never reads or writes
$_SESSION/$_GET, so it works under long-running workers (RoadRunner, Swoole, FrankenPHP), not just PHP-FPM.
Installation
composer require gam6itko/oauth2-vk-id
Requires PHP 8.1+ and league/oauth2-client ^2.7 (native PKCE).
Why the two VK-specific quirks are your job
VK ID adds two things on top of a plain Authorization Code flow. This library enables them the league-idiomatic way and leaves persistence to you, so nothing is hidden in globals:
- PKCE is mandatory. The provider returns
S256fromgetPkceMethod(), so league generates thecode_challengefor the authorize URL and appends thecode_verifierto the token exchange for you. Because the verifier must survive the browser redirect, you persist it withgetPkceCode()/setPkceCode()(session, cache, DB — wherever your framework keeps request state). device_idis returned by VK on the callback and required for the token exchange. Forward it as a plain option:getAccessToken('authorization_code', ['code' => $code, 'device_id' => $deviceId]).
Usage
use Gam6itko\OAuth2\Client\Provider\VkId; $provider = new VkId([ 'clientId' => '<VK app id>', 'clientSecret' => '', // empty for a public PKCE client 'redirectUri' => 'https://example.org/login/vk/callback', 'scopes' => 'vkid.personal_info email', // string or array; default: vkid.personal_info ]); // 1. Start: build the authorize URL and stash state + PKCE verifier wherever your request state lives. $authUrl = $provider->getAuthorizationUrl(); $store->set('vk_state', $provider->getState()); $store->set('vk_pkce', $provider->getPkceCode()); header('Location: ' . $authUrl); exit; // 2. Callback: verify state, restore the PKCE verifier, exchange the code (with device_id). if ($_GET['state'] !== $store->get('vk_state')) { exit('Invalid state'); } $provider->setPkceCode($store->get('vk_pkce')); $token = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'], 'device_id' => $_GET['device_id'], ]); /** @var \Gam6itko\OAuth2\Client\Provider\VkIdResourceOwner $owner */ $owner = $provider->getResourceOwner($token); $owner->getId(); // stable VK user id (string) — use as the provider key $owner->getFirstName(); $owner->getLastName(); $owner->getEmail(); // only with the `email` scope, else null $owner->getPhone(); // only with the `phone` scope, else null $owner->getAvatar(); $owner->toArray();
$store above is a placeholder for whatever holds per-request state in your app (PSR-16 cache, a framework session,
etc.). The library deliberately does not choose one for you.
Scopes
vkid.personal_info— name, gender, avatar, birthday (default, always available).email— populatesgetEmail().phone— populatesgetPhone().
License
MIT. See LICENSE.