Skip to content

Commit

Permalink
feat(oidc support): add oidc support
Browse files Browse the repository at this point in the history
Implement #2792
  • Loading branch information
Mike Kao authored and lenaxia committed Nov 9, 2024
1 parent 0a5945a commit 8216a60
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 25 deletions.
44 changes: 29 additions & 15 deletions server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@ import express from 'express';
import * as OpenApiValidator from 'express-openapi-validator';
import type { Store } from 'express-session';
import session from 'express-session';
import _ from 'lodash';
import next from 'next';
import path from 'path';
import swaggerUi from 'swagger-ui-express';
import YAML from 'yamljs';
import xss from 'xss';
import validator from 'validator';

const API_SPEC_PATH = path.join(__dirname, '../overseerr-api.yml');

Expand All @@ -46,19 +45,34 @@ const app = next({ dev });
const handle = app.getRequestHandler();

const logMiddleware = (req: Request, res: Response, next: NextFunction) => {
// Log information about the incoming request
logger.debug(`Request Method: ${xss(req.method)}`);
logger.debug(`Request URL: ${xss(req.url)}`);

const sanitizedHeaders = JSON.stringify(req.headers, (key, value) =>
typeof value === 'string' ? validator.escape(value) : value
);
logger.debug(`Request Headers: ${sanitizedHeaders}`);

const sanitizedBody = JSON.stringify(req.body, (key, value) =>
typeof value === 'string' ? validator.escape(value) : value
);
logger.debug(`Request Body: ${sanitizedBody}`);
// Enhanced sanitization function with explicit return type
const sanitize = (input: unknown, depth = 0): string => {
if (depth > 2) {
return typeof input === 'string' ? _.escape(input) : '[Complex Object]';
}
if (typeof input === 'string') {
return _.escape(_.truncate(input, { length: 200 }));
} else if (_.isObject(input)) {
return _.mapValues(input, (value) =>
sanitize(value, depth + 1)
) as unknown as string;
}
return input as string;
};

// Function to selectively log properties with explicit parameter types
const safeLog = (key: string, value: unknown): unknown => {
const sensitiveKeys = ['authorization', 'cookie', 'set-cookie'];
if (sensitiveKeys.includes(key.toLowerCase())) {
return '[Filtered]';
}
return sanitize(value);
};

logger.debug(`Request Method: ${sanitize(req.method)}`);
logger.debug(`Request URL: ${sanitize(req.originalUrl)}`);
logger.debug(`Request Headers: ${JSON.stringify(req.headers, safeLog)}`);
logger.debug(`Request Body: ${JSON.stringify(req.body, safeLog)}`);

next();
};
Expand Down
28 changes: 18 additions & 10 deletions server/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@ import { Permission } from '@server/lib/permissions';
import { getSettings } from '@server/lib/settings';
import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth';
import { Router} from 'express';
import type { Request } from 'express';
/* eslint-disable @typescript-eslint/no-unused-vars */
import type { OIDCJwtPayload } from '@server/utils/oidc';
import {
createJwtSchema,
getOIDCRedirectUrl,
getOIDCWellknownConfiguration,
} from '@server/utils/oidc';
import type { OIDCJwtPayload } from '@server/utils/oidc';
import { randomBytes } from 'crypto';
import type { Request } from 'express';
import { Router } from 'express';
import gravatarUrl from 'gravatar-url';
import decodeJwt from 'jwt-decode';
import type { InferType } from 'yup';
Expand Down Expand Up @@ -458,11 +457,13 @@ authRoutes.get('/oidc-login', async (req, res, next) => {
}
});


authRoutes.get('/oidc-callback', async (req, res, next) => {
try {
const logRequestInfo = (req: Request) => {
const remoteIp = req.headers['x-real-ip'] || req.headers['x-forwarded-for'] || req.connection.remoteAddress;
const remoteIp =
req.headers['x-real-ip'] ||
req.headers['x-forwarded-for'] ||
req.connection.remoteAddress;
const requestInfo = {
method: req.method,
url: req.url,
Expand Down Expand Up @@ -517,7 +518,10 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {

const wellKnownInfo = await getOIDCWellknownConfiguration(oidcDomain);

const callbackUrl = new URL('/api/v1/auth/oidc-callback', `${req.protocol}://${req.headers.host}`);
const callbackUrl = new URL(
'/api/v1/auth/oidc-callback',
`${req.protocol}://${req.headers.host}`
);
const formData = new URLSearchParams();
formData.append('client_secret', oidcClientSecret);
formData.append('grant_type', 'authorization_code');
Expand All @@ -530,7 +534,9 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {

const response = await fetch(wellKnownInfo.token_endpoint, {
method: 'POST',
headers: new Headers([['Content-Type', 'application/x-www-form-urlencoded']]),
headers: new Headers([
['Content-Type', 'application/x-www-form-urlencoded'],
]),
body: formData,
});

Expand All @@ -547,7 +553,10 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {
return res.redirect('/login');
}

const { id_token: idToken } = body as Extract<typeof body, { id_token: string }>;
const { id_token: idToken } = body as Extract<
typeof body,
{ id_token: string }
>;
try {
const decoded = decodeJwt<OIDCJwtPayload>(idToken);
const jwtSchema = createJwtSchema({
Expand Down Expand Up @@ -619,5 +628,4 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {
}
});


export default authRoutes;

0 comments on commit 8216a60

Please sign in to comment.