Защита 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 всегда проверяйте:
- Подпись -- верифицируйте по публичным ключам JWKS
- Срок действия (
exp) -- отклоняйте истекшие токены - Издатель (
iss) -- должен совпадать с URL вашего realm в VoxKey - Аудитория (
aud) -- должна совпадать с indicator вашего API Resource - Скоупы (
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}
Полученный токен валидируется точно так же, как пользовательские токены.