Refactor auth routes: explicit dependency injection
- Updated all auth route modules to use destructured dependencies - Added JSDoc comments for factory functions - Replaced ctx. references with direct parameter access - Updated auth/index.js to extract and pass explicit dependencies - sso-gate.js maintains session helper exports from session-handlers - All files pass syntax validation Files refactored: - routes/auth/keys.js - routes/auth/session-handlers.js - routes/auth/sso-gate.js - routes/auth/totp.js - routes/auth/index.js (orchestrator)
This commit is contained in:
@@ -2,18 +2,26 @@ const express = require('express');
|
||||
const { SESSION_TTL, APP, PLEX, TIMEOUTS, buildMediaAuth } = require('../../constants');
|
||||
const { AuthenticationError, NotFoundError } = require('../errors');
|
||||
|
||||
module.exports = function(ctx, getAppSession, appSessionCache) {
|
||||
/**
|
||||
* Auth SSO gate routes factory
|
||||
* @param {Object} deps - Explicit dependencies (includes session helpers)
|
||||
* @returns {express.Router}
|
||||
*/
|
||||
module.exports = function(deps) {
|
||||
const router = express.Router();
|
||||
|
||||
// Extract dependencies
|
||||
const { authManager, totpConfig, session, asyncHandler, errorResponse, log, getAppSession, appSessionCache } = deps;
|
||||
|
||||
// Caddy forward_auth gate: checks TOTP session + injects service credentials
|
||||
router.get('/auth/gate/:serviceId', ctx.asyncHandler(async (req, res) => {
|
||||
router.get('/auth/gate/:serviceId', asyncHandler(async (req, res) => {
|
||||
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate');
|
||||
const serviceId = req.params.serviceId;
|
||||
|
||||
// Check TOTP session first
|
||||
if (ctx.totpConfig.enabled && ctx.totpConfig.sessionDuration !== 'never') {
|
||||
const valid = ctx.session.isValid(req);
|
||||
if (!valid) return ctx.errorResponse(res, 401, 'Session expired or invalid', { authenticated: false });
|
||||
if (totpConfig.enabled && totpConfig.sessionDuration !== 'never') {
|
||||
const valid = session.isValid(req);
|
||||
if (!valid) return errorResponse(res, 401, 'Session expired or invalid', { authenticated: false });
|
||||
}
|
||||
|
||||
// Session valid (or TOTP disabled) - inject credentials if premium SSO is active
|
||||
@@ -73,18 +81,18 @@ module.exports = function(ctx, getAppSession, appSessionCache) {
|
||||
const apiKey = arrKey || svcKey;
|
||||
if (apiKey) { res.setHeader('X-Api-Key', apiKey); injected = true; }
|
||||
} catch (e) {
|
||||
ctx.log.warn('auth', 'Credential error', { serviceId, error: e.message });
|
||||
log.warn('auth', 'Credential error', { serviceId, error: e.message });
|
||||
}
|
||||
|
||||
res.status(200).json({ authenticated: true, credentialsInjected: injected });
|
||||
}, 'auth-gate'));
|
||||
|
||||
// Return cached app session token for client-side auth (Premium SSO feature)
|
||||
router.get('/auth/app-token/:serviceId', ctx.licenseManager.requirePremium('sso'), ctx.asyncHandler(async (req, res) => {
|
||||
router.get('/auth/app-token/:serviceId', ctx.licenseManager.requirePremium('sso'), asyncHandler(async (req, res) => {
|
||||
const { serviceId } = req.params;
|
||||
|
||||
if (ctx.totpConfig.enabled && ctx.totpConfig.sessionDuration !== 'never') {
|
||||
if (!ctx.session.isValid(req)) throw new AuthenticationError('Not authenticated');
|
||||
if (totpConfig.enabled && totpConfig.sessionDuration !== 'never') {
|
||||
if (!session.isValid(req)) throw new AuthenticationError('Not authenticated');
|
||||
}
|
||||
|
||||
// Jellyfin/Emby: separate browser-specific token
|
||||
@@ -92,7 +100,7 @@ module.exports = function(ctx, getAppSession, appSessionCache) {
|
||||
const browserCacheKey = `${serviceId}_browser`;
|
||||
const browserCached = appSessionCache.get(browserCacheKey);
|
||||
if (browserCached && browserCached.exp > Date.now()) {
|
||||
if (browserCached.failed) return ctx.errorResponse(res, 500, 'Login recently failed');
|
||||
if (browserCached.failed) return errorResponse(res, 500, 'Login recently failed');
|
||||
if (browserCached.token) {
|
||||
const resp = { token: browserCached.token };
|
||||
if (browserCached.tokenData) Object.assign(resp, browserCached.tokenData);
|
||||
@@ -118,17 +126,17 @@ module.exports = function(ctx, getAppSession, appSessionCache) {
|
||||
appSessionCache.set(browserCacheKey, { token: authData.AccessToken, tokenData, exp: Date.now() + SESSION_TTL.TOKEN_SESSION });
|
||||
return res.json({ token: authData.AccessToken, ...tokenData });
|
||||
}
|
||||
return ctx.errorResponse(res, 500, '[DC-501] Authentication failed');
|
||||
return errorResponse(res, 500, '[DC-501] Authentication failed');
|
||||
} catch (e) {
|
||||
ctx.log.warn('auth', 'Browser token error', { serviceId, error: e.message });
|
||||
return ctx.errorResponse(res, 500, e.message);
|
||||
log.warn('auth', 'Browser token error', { serviceId, error: e.message });
|
||||
return errorResponse(res, 500, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// Check cache first
|
||||
const cached = appSessionCache.get(serviceId);
|
||||
if (cached && cached.exp > Date.now()) {
|
||||
if (cached.failed) return ctx.errorResponse(res, 500, '[DC-501] Login recently failed, retrying in a few minutes');
|
||||
if (cached.failed) return errorResponse(res, 500, '[DC-501] Login recently failed, retrying in a few minutes');
|
||||
if (cached.token) {
|
||||
const resp = { token: cached.token };
|
||||
if (cached.tokenData) Object.assign(resp, cached.tokenData);
|
||||
@@ -172,10 +180,10 @@ module.exports = function(ctx, getAppSession, appSessionCache) {
|
||||
return res.json({ cookies: appCookies });
|
||||
}
|
||||
|
||||
ctx.errorResponse(res, 500, '[DC-501] Login failed');
|
||||
errorResponse(res, 500, '[DC-501] Login failed');
|
||||
} catch (e) {
|
||||
ctx.log.warn('auth', 'App-token error', { error: e.message });
|
||||
ctx.errorResponse(res, 500, e.message);
|
||||
log.warn('auth', 'App-token error', { error: e.message });
|
||||
errorResponse(res, 500, e.message);
|
||||
}
|
||||
}, 'auth-app-token'));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user