Compare commits

...

2 Commits

Author SHA1 Message Date
fallenbagel
6caa0456bf feat: use gravatar for jellyfin users' with missing jellyfin avatars 2024-02-23 09:26:45 +05:00
fallenbagel
d9da3a2e3f refactor: jellyfin authentication
This refactor standardizes the authentication approach in Jellyfin to mirror the method employed in
Plex authentication for consistency
2024-02-23 09:18:13 +05:00
3 changed files with 101 additions and 64 deletions

View File

@@ -11,6 +11,7 @@ import logger from '@server/logger';
import { isAuthenticated } from '@server/middleware/auth'; import { isAuthenticated } from '@server/middleware/auth';
import * as EmailValidator from 'email-validator'; import * as EmailValidator from 'email-validator';
import { Router } from 'express'; import { Router } from 'express';
import gravatarUrl from 'gravatar-url';
const authRoutes = Router(); const authRoutes = Router();
@@ -274,24 +275,82 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
where: { jellyfinUserId: account.User.Id }, where: { jellyfinUserId: account.User.Id },
}); });
if (user) { if (!user && !(await userRepository.count())) {
logger.info(
'Sign-in attempt from Jellyfin user with access to the media server; creating initial admin user for Overseerr',
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);
// User doesn't exist, and there are no users in the database, we'll create the user
// with admin permission
settings.main.mediaServerType = MediaServerType.JELLYFIN;
user = new User({
email: body.email,
jellyfinUsername: account.User.Name,
jellyfinUserId: account.User.Id,
jellyfinDeviceId: deviceId,
jellyfinAuthToken: account.AccessToken,
permissions: Permission.ADMIN,
avatar: account.User.PrimaryImageTag
? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`
: gravatarUrl(body.email ?? '', { default: 'mm', size: 200 }),
userType: UserType.JELLYFIN,
});
settings.jellyfin.hostname = body.hostname ?? '';
settings.jellyfin.serverId = account.User.ServerId;
settings.save();
startJobs();
await userRepository.save(user);
}
// User already exists, let's update their information
else if (body.username === user?.jellyfinUsername) {
logger.info(
`Found matching ${
settings.main.mediaServerType === MediaServerType.JELLYFIN
? 'Jellyfin'
: 'Emby'
} user; updating user with ${
settings.main.mediaServerType === MediaServerType.JELLYFIN
? 'Jellyfin'
: 'Emby'
}`,
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);
// Let's check if their authtoken is up to date // Let's check if their authtoken is up to date
if (user.jellyfinAuthToken !== account.AccessToken) { if (user.jellyfinAuthToken !== account.AccessToken) {
user.jellyfinAuthToken = account.AccessToken; user.jellyfinAuthToken = account.AccessToken;
} }
// Update the users avatar with their jellyfin profile pic (incase it changed) // Update the users avatar with their jellyfin profile pic (incase it changed)
if (account.User.PrimaryImageTag) { if (account.User.PrimaryImageTag) {
user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`; user.avatar = `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`;
} else { } else {
user.avatar = '/os_logo_square.png'; user.avatar = gravatarUrl(user.email, {
default: 'mm',
size: 200,
});
} }
user.jellyfinUsername = account.User.Name; user.jellyfinUsername = account.User.Name;
if (user.username === account.User.Name) { if (user.username === account.User.Name) {
user.username = ''; user.username = '';
} }
// If JELLYFIN_TYPE is set to 'emby' then set mediaServerType to EMBY
if (process.env.JELLYFIN_TYPE === 'emby') {
settings.main.mediaServerType = MediaServerType.EMBY;
settings.save();
}
await userRepository.save(user); await userRepository.save(user);
} else if (!settings.main.newPlexLogin) { } else if (!settings.main.newPlexLogin) {
logger.warn( logger.warn(
@@ -307,69 +366,38 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
status: 403, status: 403,
message: 'Access denied.', message: 'Access denied.',
}); });
} else { } else if (!user) {
// Here we check if it's the first user. If it is, we create the user with no check logger.info(
// and give them admin permissions 'Sign-in attempt from Jellyfin user with access to the media server; creating new Overseerr user',
const totalUsers = await userRepository.count(); {
if (totalUsers === 0) { label: 'API',
logger.info( ip: req.ip,
'Sign-in attempt from Jellyfin user with access to the media server; creating initial admin user for Overseerr',
{
label: 'API',
ip: req.ip,
jellyfinUsername: account.User.Name,
}
);
user = new User({
email: body.email,
jellyfinUsername: account.User.Name, jellyfinUsername: account.User.Name,
jellyfinUserId: account.User.Id,
jellyfinDeviceId: deviceId,
jellyfinAuthToken: account.AccessToken,
permissions: Permission.ADMIN,
avatar: account.User.PrimaryImageTag
? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`
: '/os_logo_square.png',
userType: UserType.JELLYFIN,
});
await userRepository.save(user);
//Update hostname in settings if it doesn't exist (initial configuration)
//Also set mediaservertype to JELLYFIN
if (settings.jellyfin.hostname === '') {
settings.main.mediaServerType = MediaServerType.JELLYFIN;
settings.jellyfin.hostname = body.hostname ?? '';
settings.jellyfin.serverId = account.User.ServerId;
settings.save();
startJobs();
} }
);
if (!body.email) {
throw new Error('add_email');
} }
if (!user) { user = new User({
if (!body.email) { email: body.email,
throw new Error('add_email'); jellyfinUsername: account.User.Name,
} jellyfinUserId: account.User.Id,
jellyfinDeviceId: deviceId,
user = new User({ jellyfinAuthToken: account.AccessToken,
email: body.email, permissions: settings.main.defaultPermissions,
jellyfinUsername: account.User.Name, avatar: account.User.PrimaryImageTag
jellyfinUserId: account.User.Id, ? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90`
jellyfinDeviceId: deviceId, : gravatarUrl(body.email, { default: 'mm', size: 200 }),
jellyfinAuthToken: account.AccessToken, userType: UserType.JELLYFIN,
permissions: settings.main.defaultPermissions, });
avatar: account.User.PrimaryImageTag //initialize Jellyfin/Emby users with local login
? `${jellyfinHost}/Users/${account.User.Id}/Images/Primary/?tag=${account.User.PrimaryImageTag}&quality=90` const passedExplicitPassword = body.password && body.password.length > 0;
: '/os_logo_square.png', if (passedExplicitPassword) {
userType: UserType.JELLYFIN, await user.setPassword(body.password ?? '');
});
//initialize Jellyfin/Emby users with local login
const passedExplicitPassword =
body.password && body.password.length > 0;
if (passedExplicitPassword) {
await user.setPassword(body.password ?? '');
}
await userRepository.save(user);
} }
await userRepository.save(user);
} }
// Set logged in session // Set logged in session
@@ -400,6 +428,11 @@ authRoutes.post('/jellyfin', async (req, res, next) => {
status: 406, status: 406,
message: 'CREDENTIAL_ERROR_ADD_EMAIL', message: 'CREDENTIAL_ERROR_ADD_EMAIL',
}); });
} else if (e.message === 'select_server_type') {
return next({
status: 406,
message: 'CREDENTIAL_ERROR_NO_SERVER_TYPE',
});
} else { } else {
logger.error(e.message, { label: 'Auth' }); logger.error(e.message, { label: 'Auth' });
return next({ return next({

View File

@@ -29,6 +29,7 @@ import { getAppVersion } from '@server/utils/appVersion';
import { Router } from 'express'; import { Router } from 'express';
import rateLimit from 'express-rate-limit'; import rateLimit from 'express-rate-limit';
import fs from 'fs'; import fs from 'fs';
import gravatarUrl from 'gravatar-url';
import { escapeRegExp, merge, omit, set, sortBy } from 'lodash'; import { escapeRegExp, merge, omit, set, sortBy } from 'lodash';
import { rescheduleJob } from 'node-schedule'; import { rescheduleJob } from 'node-schedule';
import path from 'path'; import path from 'path';
@@ -337,7 +338,7 @@ settingsRoutes.get('/jellyfin/users', async (req, res) => {
id: user.Id, id: user.Id,
thumb: user.PrimaryImageTag thumb: user.PrimaryImageTag
? `${jellyfinHost}/Users/${user.Id}/Images/Primary/?tag=${user.PrimaryImageTag}&quality=90` ? `${jellyfinHost}/Users/${user.Id}/Images/Primary/?tag=${user.PrimaryImageTag}&quality=90`
: '/os_logo_square.png', : gravatarUrl(user.Name, { default: 'mm', size: 200 }),
email: user.Name, email: user.Name,
})); }));

View File

@@ -537,7 +537,10 @@ router.post(
permissions: settings.main.defaultPermissions, permissions: settings.main.defaultPermissions,
avatar: jellyfinUser?.PrimaryImageTag avatar: jellyfinUser?.PrimaryImageTag
? `${jellyfinHost}/Users/${jellyfinUser.Id}/Images/Primary/?tag=${jellyfinUser.PrimaryImageTag}&quality=90` ? `${jellyfinHost}/Users/${jellyfinUser.Id}/Images/Primary/?tag=${jellyfinUser.PrimaryImageTag}&quality=90`
: '/os_logo_square.png', : gravatarUrl(jellyfinUser?.Name ?? '', {
default: 'mm',
size: 200,
}),
userType: UserType.JELLYFIN, userType: UserType.JELLYFIN,
}); });