Введение: зачем нужны OAuth 2.0 и JWT в PHP
Современные веб-приложения редко работают изолированно. Пользователи ожидают единого входа через Google, Facebook или GitHub, а разработчики — безопасного обмена данными между микросервисами. Два ключевых протокола — OAuth 2.0 (делегирование доступа) и JWT (компактный формат токена) — стали стандартом де-факто. В этой статье мы разберём, как реализовать OAuth 2.0 и JWT авторизацию на PHP с нуля, используя только встроенные возможности языка и популярные библиотеки. Вы получите готовые примеры кода для интеграции с внешними провайдерами и создания собственной системы аутентификации.
1. Основы OAuth 2.0: потоки и роли
OAuth 2.0 — это протокол авторизации, который позволяет сторонним приложениям получать ограниченный доступ к ресурсам пользователя без передачи пароля. В нём участвуют четыре роли:
- Resource Owner — пользователь, владеющий данными.
- Client — ваше PHP-приложение, запрашивающее доступ.
- Authorization Server — сервер, выдающий токены (например, Google).
- Resource Server — API, предоставляющее данные по токену.
Самый популярный поток для веб-приложений — Authorization Code Grant. Он безопасен, так как токен передаётся через серверную часть, а не через браузер. Рассмотрим реализацию на PHP.
1.1. Регистрация приложения и получение Client ID/Secret
Перед написанием кода зарегистрируйте приложение у провайдера (например, в Google Cloud Console). Вы получите client_id и client_secret, а также настроите redirect URI (например, https://вашсайт.ru/callback.php).
1.2. Реализация потока Authorization Code на PHP
Создадим два файла: login.php (перенаправление на сервер авторизации) и callback.php (обработка ответа).
Файл login.php
<?php
session_start();
$client_id = 'ВАШ_CLIENT_ID';
$redirect_uri = 'https://вашсайт.ru/callback.php';
$state = bin2hex(random_bytes(16)); // защита от CSRF
$_SESSION['oauth_state'] = $state;
$params = http_build_query([
'client_id' => $client_id,
'redirect_uri' => $redirect_uri,
'response_type' => 'code',
'scope' => 'openid email profile',
'state' => $state,
]);
header('Location: https://accounts.google.com/o/oauth2/v2/auth?' . $params);
exit;
?>
Файл callback.php
<?php
session_start();
// Проверка state (CSRF)
if ($_GET['state'] !== $_SESSION['oauth_state']) {
die('Ошибка: неверный state');
}
$client_id = 'ВАШ_CLIENT_ID';
$client_secret = 'ВАШ_CLIENT_SECRET';
$redirect_uri = 'https://вашсайт.ru/callback.php';
// Обмен кода на токен
$ch = curl_init('https://oauth2.googleapis.com/token');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'code' => $_GET['code'],
'client_id' => $client_id,
'client_secret' => $client_secret,
'redirect_uri' => $redirect_uri,
'grant_type' => 'authorization_code',
]));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
$access_token = $data['access_token'] ?? null;
// Получение профиля пользователя
$ch = curl_init('https://www.googleapis.com/oauth2/v2/userinfo');
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Authorization: Bearer ' . $access_token]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$userInfo = json_decode(curl_exec($ch), true);
curl_close($ch);
echo 'Привет, ' . htmlspecialchars($userInfo['email']);
?>
Важно: Всегда проверяйте параметр state. Он защищает от подделки запроса (CSRF).
2. JWT (JSON Web Token): структура и создание на PHP
JWT — это компактный и самодостаточный токен, который состоит из трёх частей: Header, Payload и Signature. Он идеально подходит для stateless-авторизации: сервер не хранит сессии, а проверяет подпись токена. На PHP мы можем создать JWT с помощью библиотеки firebase/php-jwt или руками.
2.1. Установка библиотеки через Composer
composer require firebase/php-jwt
2.2. Создание JWT токена
<?php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
$secret_key = 'ваш-секретный-ключ-длиной-не-менее-32-символа';
$payload = [
'iss' => 'https://вашсайт.ru', // издатель
'iat' => time(), // время выпуска
'exp' => time() + 3600, // срок действия (1 час)
'sub' => 123, // ID пользователя
'email' => 'user@example.com',
];
$jwt = JWT::encode($payload, $secret_key, 'HS256');
echo $jwt;
?>
2.3. Проверка и декодирование JWT
<?php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
$secret_key = 'ваш-секретный-ключ-длиной-не-менее-32-символа';
$jwt = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; // Bearer token из заголовка
try {
$decoded = JWT::decode($jwt, new Key($secret_key, 'HS256'));
echo 'Пользователь ID: ' . $decoded->sub;
} catch (\Exception $e) {
http_response_code(401);
echo 'Ошибка авторизации: ' . $e->getMessage();
}
?>