Перейти к основному содержимому

Защита API

Валидируйте JWT-токены VoxKey в вашем бэкенде для защиты API-эндпоинтов.

Вариант 1: JWT-валидация (рекомендуется)

Проверяйте токены локально, используя публичные ключи realm. Сетевые вызовы не нужны.

Получение JWKS

curl https://your-domain.com/oauth2/{realmUUID}/oidc/jwks

Node.js (Express)

import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({
jwksUri: 'https://your-domain.com/oauth2/{realmUUID}/oidc/jwks',
cache: true,
rateLimit: true,
});

function getKey(header, callback) {
client.getSigningKey(header.kid, (err, key) => {
callback(err, key?.getPublicKey());
});
}

function authMiddleware(req, res, next) {
const token = req.headers.authorization?.replace('Bearer ', '');
if (!token) return res.status(401).json({ error: 'No token' });

jwt.verify(token, getKey, { algorithms: ['RS256'] }, (err, decoded) => {
if (err) return res.status(401).json({ error: 'Invalid token' });
req.user = decoded;
next();
});
}

// Проверка скоупов
function requireScope(scope) {
return (req, res, next) => {
const scopes = req.user.scope?.split(' ') || [];
if (!scopes.includes(scope)) {
return res.status(403).json({ error: 'Insufficient scope' });
}
next();
};
}

// Использование
app.get('/api/posts', authMiddleware, requireScope('read:posts'), (req, res) => {
// req.user содержит декодированный JWT
});

PHP (Laravel)

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;

class VoxKeyMiddleware
{
private static ?array $jwks = null;

public function handle($request, Closure $next, string $scope = null)
{
$token = $request->bearerToken();
if (!$token) {
return response()->json(['error' => 'No token'], 401);
}

try {
$keys = $this->getJwks();
$decoded = JWT::decode($token, JWK::parseKeySet($keys));
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid token'], 401);
}

if ($scope) {
$scopes = explode(' ', $decoded->scope ?? '');
if (!in_array($scope, $scopes)) {
return response()->json(['error' => 'Insufficient scope'], 403);
}
}

$request->attributes->set('jwt_user', $decoded);
return $next($request);
}

private function getJwks(): array
{
if (self::$jwks) return self::$jwks;

$response = file_get_contents(
'https://your-domain.com/oauth2/{realmUUID}/oidc/jwks'
);
self::$jwks = json_decode($response, true);
return self::$jwks;
}
}

// В маршрутах
Route::get('/posts', [PostController::class, 'index'])
->middleware('voxkey:read:posts');

Вариант 2: Token Introspection

Используйте эндпоинт интроспекции (RFC 7662) для проверки валидности токена на стороне сервера. Полезно, когда нужна проверка отзыва токенов в реальном времени.

curl -X POST https://your-domain.com/oauth2/{realmUUID}/introspect \
-d token=ACCESS_TOKEN \
-u CLIENT_ID:CLIENT_SECRET

Ответ:

{
"active": true,
"sub": "user_abc123",
"client_id": "your-app",
"scope": "openid profile read:posts",
"exp": 1711612800,
"iat": 1711609200
}

Если токен отозван или истек, active будет false.

Чек-лист валидации токенов

При проверке JWT всегда проверяйте:

  1. Подпись -- верифицируйте по публичным ключам JWKS
  2. Срок действия (exp) -- отклоняйте истекшие токены
  3. Издатель (iss) -- должен совпадать с URL вашего realm в VoxKey
  4. Аудитория (aud) -- должна совпадать с indicator вашего API Resource
  5. Скоупы (scope) -- проверьте наличие нужного разрешения

Отзыв токенов

Отзыв токенов по RFC 7009:

curl -X POST https://your-domain.com/oauth2/{realmUUID}/revoke \
-d token=REFRESH_TOKEN \
-d token_type_hint=refresh_token \
-u CLIENT_ID:CLIENT_SECRET

M2M-токены (Machine-to-Machine)

Для бэкенд-сервисов используйте Client Credentials grant:

curl -X POST https://your-domain.com/oauth2/{realmUUID}/token \
-d grant_type=client_credentials \
-d client_id=SERVICE_CLIENT_ID \
-d client_secret=SERVICE_SECRET \
-d scope="users:read users:write" \
-d resource=https://your-domain.com/api/v1/{realmUUID}

Полученный токен валидируется точно так же, как пользовательские токены.