v0.5.14 — вход по токену, JWKS в mock-режиме, production deploy docs
- POST /login-token: принимает реальный JWT (не только clientId) - auth.js: verifyAnyToken — mock + JWKS (Keycloak Nubes) одновременно - auth.js: JWKS предзагружается всегда, даже в mock-режиме - devops-deploy.md: полная production-инструкция для DevOps - .env.example: production-ready шаблон - README.md: актуальный (v0.5.13, архитектура, тесты, ссылки) - .gitignore: очищен от дубликатов
This commit is contained in:
+41
-16
@@ -66,16 +66,17 @@ async function initAuth() {
|
||||
|
||||
const isOidc = !!(KC_CLIENT_ID && KC_CLIENT_SECRET);
|
||||
|
||||
// Всегда предзагружаем JWKS (даже в mock-режиме), чтобы можно было
|
||||
// проверить реальный Bearer токен от Keycloak без KC_CLIENT_ID/SECRET.
|
||||
try {
|
||||
cachedJwks = JSON.parse(await httpGet(OIDC.certs));
|
||||
console.log('[auth] JWKS loaded:', cachedJwks.keys.length, 'ключей');
|
||||
} catch (e) {
|
||||
console.warn('[auth] JWKS preload failed:', e.message, '— только mock-режим');
|
||||
}
|
||||
|
||||
if (isOidc) {
|
||||
console.log('[auth] OIDC — Keycloak', KC_BASE_URL);
|
||||
// Предзагрузка JWKS при старте — избегаем задержки на первом запросе.
|
||||
// При ошибке логируем предупреждение: JWKS будет загружен в exchangeCode().
|
||||
try {
|
||||
cachedJwks = JSON.parse(await httpGet(OIDC.certs));
|
||||
console.log('[auth] JWKS loaded:', cachedJwks.keys.length, 'ключей');
|
||||
} catch (e) {
|
||||
console.warn('[auth] JWKS preload failed:', e.message, '— будет загружен при первом запросе');
|
||||
}
|
||||
} else {
|
||||
console.log('[auth] MOCK — local RS256 key');
|
||||
mockKeyPair = crypto.generateKeyPairSync('rsa', {
|
||||
@@ -122,7 +123,7 @@ function createMiddleware(isOidc) {
|
||||
const bearer = (req.headers.authorization || '').replace(/^Bearer\s+/i, '').trim();
|
||||
if (bearer) {
|
||||
try {
|
||||
const payload = isOidc ? verifyOidcToken(bearer) : verifyMockToken(bearer);
|
||||
const payload = verifyAnyToken(bearer);
|
||||
req.user = userFromPayload(payload);
|
||||
return next();
|
||||
} catch (e) {
|
||||
@@ -234,12 +235,36 @@ function verifyOidcToken(token) {
|
||||
if (!key) throw new Error('No matching RSA sig key in JWKS' + (kid ? ` (kid=${kid})` : ''));
|
||||
|
||||
const pem = crypto.createPublicKey({ key, format: 'jwk' }).export({ type: 'spki', format: 'pem' });
|
||||
// Проверяем issuer и audience — защита от токенов других realm/клиентов (A02).
|
||||
return jwt.verify(token, pem, {
|
||||
algorithms: ['RS256'],
|
||||
issuer: KC_BASE_URL,
|
||||
audience: KC_CLIENT_ID,
|
||||
});
|
||||
// Проверяем issuer — защита от токенов других realm.
|
||||
// audience проверяем только если задан KC_CLIENT_ID (для OIDC-режима).
|
||||
const opts = { algorithms: ['RS256'], issuer: KC_BASE_URL };
|
||||
if (KC_CLIENT_ID) opts.audience = KC_CLIENT_ID;
|
||||
return jwt.verify(token, pem, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Универсальная верификация: пробует mock, затем OIDC (JWKS).
|
||||
* Позволяет принимать и mock-токены (тесты, dev), и реальные токены от Keycloak.
|
||||
*/
|
||||
function verifyAnyToken(token) {
|
||||
let lastErr = null;
|
||||
// 1. Mock (быстрый, локальный)
|
||||
if (mockKeyPair) {
|
||||
try {
|
||||
return jwt.verify(token, mockKeyPair.publicKey, { algorithms: ['RS256'], issuer: ISSUER });
|
||||
} catch (e) {
|
||||
lastErr = e;
|
||||
}
|
||||
}
|
||||
// 2. OIDC (JWKS от Keycloak)
|
||||
if (cachedJwks) {
|
||||
try {
|
||||
return verifyOidcToken(token);
|
||||
} catch (e) {
|
||||
lastErr = e;
|
||||
}
|
||||
}
|
||||
throw lastErr || new Error('Token verification failed: no verifier available');
|
||||
}
|
||||
|
||||
function userFromPayload(payload) {
|
||||
@@ -269,7 +294,7 @@ function createBearerMiddleware(isOidc) {
|
||||
return res.status(401).json({ error: 'Bearer token required' });
|
||||
}
|
||||
try {
|
||||
const payload = isOidc ? verifyOidcToken(bearer) : verifyMockToken(bearer);
|
||||
const payload = verifyAnyToken(bearer);
|
||||
req.user = userFromPayload(payload);
|
||||
return next();
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user