okay trying again with no error handling changes. Just logging and scope support

pull/3743/head
Mike Kao 1 year ago
parent de15f9017a
commit df8c1b53e5

@ -446,45 +446,62 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {
const settings = getSettings(); const settings = getSettings();
const { oidcDomain, oidcClientId, oidcClientSecret } = settings.main; const { oidcDomain, oidcClientId, oidcClientSecret } = settings.main;
// Log the initial OIDC callback request
logger.info('Received OIDC callback', { ip: req.ip });
if (!settings.main.oidcLogin) { if (!settings.main.oidcLogin) {
logger.warn('OIDC sign-in is disabled', { ip: req.ip });
return res.status(500).json({ error: 'OIDC sign-in is disabled.' }); return res.status(500).json({ error: 'OIDC sign-in is disabled.' });
} }
const cookieState = req.cookies['oidc-state']; const cookieState = req.cookies['oidc-state'];
const url = new URL(req.url, `${req.protocol}://${req.hostname}`); const url = new URL(req.url, `${req.protocol}://${req.hostname}`);
const state = url.searchParams.get('state'); const state = url.searchParams.get('state');
const scope = url.searchParams.get('scope'); // Handling 'scope' parameter const scope = url.searchParams.get('scope'); // Optional scope parameter
// Optional logging for scope parameter
if (scope) {
logger.info('OIDC callback with scope', { scope });
} else {
logger.info('OIDC callback without scope');
}
try { try {
// State validation // Check that the request belongs to the correct state
if (!state || cookieState !== state) { if (state && cookieState === state) {
logger.warn('OIDC state mismatch', { ip: req.ip, state, cookieState }); res.clearCookie('oidc-state');
} else {
logger.info('Failed OIDC login attempt', {
cause: 'Invalid state',
ip: req.ip,
state: state,
cookieState: cookieState,
});
return res.redirect('/login'); return res.redirect('/login');
} }
res.clearCookie('oidc-state');
// Code validation // Check that a code as been issued
const code = url.searchParams.get('code'); const code = url.searchParams.get('code');
if (!code) { if (!code) {
logger.warn('OIDC code missing', { ip: req.ip }); logger.info('Failed OIDC login attempt', {
cause: 'Invalid code',
ip: req.ip,
code: code,
});
return res.redirect('/login'); return res.redirect('/login');
} }
const wellKnownInfo = await getOIDCWellknownConfiguration(oidcDomain); const wellKnownInfo = await getOIDCWellknownConfiguration(oidcDomain);
// Token request // Fetch the token data
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(); const formData = new URLSearchParams();
formData.append('client_secret', oidcClientSecret); formData.append('client_secret', oidcClientSecret);
formData.append('grant_type', 'authorization_code'); formData.append('grant_type', 'authorization_code');
formData.append('redirect_uri', callbackUrl.toString()); formData.append('redirect_uri', callbackUrl.toString());
formData.append('client_id', oidcClientId); formData.append('client_id', oidcClientId);
formData.append('code', code); formData.append('code', code);
if (scope) { // Append 'scope' only if it's provided // Append scope if available
if (scope) {
formData.append('scope', scope); formData.append('scope', scope);
} }
@ -496,50 +513,88 @@ authRoutes.get('/oidc-callback', async (req, res, next) => {
body: formData, body: formData,
}); });
// Validate response // Check that the response is valid
const body = await response.json(); const body = (await response.json()) as
| { id_token: string; error: never }
| { error: string };
if (body.error) { if (body.error) {
logger.warn('Invalid token response', { ip: req.ip, error: body.error }); logger.info('Failed OIDC login attempt', {
cause: 'Invalid token response',
ip: req.ip,
body: body,
});
return res.redirect('/login'); return res.redirect('/login');
} }
// Token validation // Validate that the token response is valid and not manipulated
const { id_token: idToken } = body; const { id_token: idToken } = body as Extract<
let decoded; typeof body,
{ id_token: string }
>;
try { try {
decoded = decodeJwt(idToken); const decoded = decodeJwt(idToken);
const jwtSchema = createJwtSchema({ oidcClientId, oidcDomain }); const jwtSchema = createJwtSchema({
oidcClientId: oidcClientId,
oidcDomain: oidcDomain,
});
await jwtSchema.validate(decoded); await jwtSchema.validate(decoded);
} catch (error) { } catch {
logger.warn('JWT validation failed', { ip: req.ip, error: error.message }); logger.info('Failed OIDC login attempt', {
cause: 'Invalid jwt',
ip: req.ip,
idToken: idToken,
});
return res.redirect('/login'); return res.redirect('/login');
} }
// Email verification // Check that email is verified and map email to user
const decoded: InferType<ReturnType<typeof createJwtSchema>> =
decodeJwt(idToken);
if (!decoded.email_verified) { if (!decoded.email_verified) {
logger.warn('Email not verified', { ip: req.ip, email: decoded.email }); logger.info('Failed OIDC login attempt', {
cause: 'Email not verified',
ip: req.ip,
email: decoded.email,
});
return res.redirect('/login'); return res.redirect('/login');
} }
// User handling
const userRepository = getRepository(User); const userRepository = getRepository(User);
let user = await userRepository.findOne({ where: { email: decoded.email } }); let user = await userRepository.findOne({
where: { email: decoded.email },
});
// Create user if it doesn't exist
if (!user) { if (!user) {
logger.info('Creating new user', { ip: req.ip, email: decoded.email }); logger.info(`Creating user for ${decoded.email}`, {
ip: req.ip,
email: decoded.email,
});
const avatar = gravatarUrl(decoded.email, { default: 'mm', size: 200 }); const avatar = gravatarUrl(decoded.email, { default: 'mm', size: 200 });
user = new User({ avatar, username: decoded.email, email: decoded.email, permissions: settings.main.defaultPermissions, plexToken: '', userType: UserType.LOCAL }); user = new User({
avatar: avatar,
username: decoded.email,
email: decoded.email,
permissions: settings.main.defaultPermissions,
plexToken: '',
userType: UserType.LOCAL,
});
await userRepository.save(user); await userRepository.save(user);
} }
// Session handling // Set logged in session and return
if (req.session) { if (req.session) {
req.session.userId = user.id; req.session.userId = user.id;
} }
logger.info('User logged in successfully', { ip: req.ip, userId: user.id });
return res.redirect('/'); return res.redirect('/');
} catch (error) { } catch (error) {
logger.error('OIDC login error', { ip: req.ip, errorMessage: error.message }); logger.error('Failed OIDC login attempt', {
cause: 'Unknown error',
ip: req.ip,
errorMessage: error.message,
});
return res.redirect('/login'); return res.redirect('/login');
} }
}); });

Loading…
Cancel
Save