Install the Package
composer require baks-dev/auth-vk
Configure VK App
https://your-app.dev/auth/vk/callback).Client ID and generate a PKCE Code Verifier (use PKCE Generator).Update .env
VK_CLIENT_ID=your_client_id_here
VK_CODE_VERIFIER=your_generated_verifier_here
Run Asset & DB Setup
php bin/console baks:assets:install
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case
Add the VK auth route to your routes/web.php:
use BaksDev\AuthVk\AuthVkController;
Route::get('/auth/vk', [AuthVkController::class, 'redirectToVk'])->name('auth.vk.redirect');
Route::get('/auth/vk/callback', [AuthVkController::class, 'handleVkCallback'])->name('auth.vk.callback');
Authentication Flow
AuthVkController::redirectToVk() (handles OAuth redirect).auth_code.User Mapping
User model with vkId and vkEmail fields (created via migrations).BaksDev\AuthVk\Services\UserMapper to customize user creation/updating:
// config/packages/baks_auth_vk.yaml
baks_auth_vk:
user_mapper: App\Services\CustomVkUserMapper
Session Handling
security.yaml includes:
firewalls:
main:
form_login: ~
vk_auth: ~ # Enables VK auth provider
Customizing Token Exchange
BaksDev\AuthVk\Services\VkAuthService to modify token requests:
public function getToken(string $code): array
{
$response = Http::post('https://oauth.vk.com/access_token', [
'client_id' => config('services.vk.client_id'),
'client_secret' => config('services.vk.client_secret'), // Add if needed
'code' => $code,
'redirect_uri' => route('auth.vk.callback'),
'grant_type' => 'authorization_code',
]);
return $response->json();
}
Multi-Tenant Support
// In your UserMapper
public function mapUser(array $vkData): User
{
$vkData['tenant_id'] = request()->tenantId; // Example
return User::updateOrCreate(['vkId' => $vkData['id']], $vkData);
}
PKCE Validation
VK_CODE_VERIFIER is exactly the same as the one generated (case-sensitive).Redirect URI Mismatch
Trusted Redirect URL in your app settings.https://your-app.test (not localhost).Missing User Fields
vkId and vkEmail to the users table. If your User model uses different field names, override the mapper.Token Expiry
// In VkAuthService
public function getFreshToken(): array
{
if ($this->isTokenExpired()) {
return $this->getToken($this->getStoredAuthCode());
}
return $this->getCachedToken();
}
Error Handling
error field (e.g., access_denied). Log these:
try {
$token = $this->getToken($code);
} catch (\GuzzleHttp\Exception\RequestException $e) {
$response = json_decode($e->getResponse()->getBody(), true);
throw new \RuntimeException($response['error_description'] ?? 'VK auth failed');
}
Enable VK Debug Mode
Add ?scope=email,offline to the auth URL to force email scope and offline access:
// In AuthVkController
public function redirectToVk()
{
return redirect()->to('https://oauth.vk.com/authorize?' .
http_build_query([
'client_id' => config('services.vk.client_id'),
'redirect_uri' => route('auth.vk.callback'),
'response_type' => 'code',
'scope' => 'email,offline',
'state' => bin2hex(random_bytes(16)),
'code_challenge' => $this->generateCodeChallenge(),
'code_challenge_method' => 'S256',
]));
}
Log VK Responses
Add logging to VkAuthService:
public function getUserData(string $accessToken): array
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . $accessToken,
])->get('https://api.vk.com/method/users.get', [
'access_token' => $accessToken,
'v' => '5.131',
'fields' => 'id,email',
]);
\Log::debug('VK API Response:', $response->json());
return $response->json();
}
Test with VK Sandbox Use VK’s sandbox mode for testing:
VK_SANDBOX_MODE=true
Custom User Provider
Implement BaksDev\AuthVk\Contracts\UserProviderInterface to fetch users from a custom source:
class CustomUserProvider implements UserProviderInterface
{
public function findByVkId(string $vkId): ?User
{
return User::where('external_id', $vkId)->first();
}
}
Post-Auth Actions Trigger events after VK auth:
// In AuthVkController
public function handleVkCallback()
{
$user = $this->authService->authenticate();
event(new VkUserAuthenticated($user));
}
Dynamic Scopes Fetch additional VK fields dynamically:
// Override VkAuthService
protected function getUserFields(): array
{
return ['id', 'email', 'first_name', 'last_name', 'photo_100'];
}
Rate Limiting Add rate limiting to VK API calls:
use Symfony\Component\RateLimiter\RateLimiterFactory;
public function __construct(private RateLimiterFactory $rateLimiter)
{}
public function getUserData(string $accessToken): array
{
$this->rateLimiter->createRateLimiter(10, 'second')->consume();
// ... rest of the method
}
How can I help you explore Laravel packages today?