mirror of
https://github.com/diced/zipline.git
synced 2025-05-10 18:05:54 +02:00
fix: sessions & authentik -> oidc & #603
This commit is contained in:
parent
708b130002
commit
67a7d44198
20 changed files with 119 additions and 107 deletions
10
README.md
10
README.md
|
@ -118,11 +118,11 @@ OAUTH_GITHUB_CLIENT_SECRET=x
|
|||
OAUTH_GOOGLE_CLIENT_ID=x-x.apps.googleusercontent.com
|
||||
OAUTH_GOOGLE_CLIENT_SECRET=x-x-x
|
||||
|
||||
OAUTH_AUTHENTIK_CLIENT_ID=x
|
||||
OAUTH_AUTHENTIK_CLIENT_SECRET=x
|
||||
OAUTH_AUTHENTIK_AUTHORIZE_URL=http://localhost:9000/application/o/authorize/
|
||||
OAUTH_AUTHENTIK_USERINFO_URL=http://localhost:9000/application/o/userinfo/
|
||||
OAUTH_AUTHENTIK_TOKEN_URL=http://localhost:9000/application/o/token/
|
||||
OAUTH_OIDC_CLIENT_ID=x
|
||||
OAUTH_OIDC_CLIENT_SECRET=x
|
||||
OAUTH_OIDC_AUTHORIZE_URL=http://localhost:9000/application/o/authorize/
|
||||
OAUTH_OIDC_USERINFO_URL=http://localhost:9000/application/o/userinfo/
|
||||
OAUTH_OIDC_TOKEN_URL=http://localhost:9000/application/o/token/
|
||||
|
||||
FEATURES_OAUTH_REGISTRATION=true
|
||||
FEATURES_USER_REGISTRATION=true
|
||||
|
|
|
@ -29,6 +29,7 @@ model User {
|
|||
|
||||
totpSecret String?
|
||||
passkeys UserPasskey[]
|
||||
sessions String[]
|
||||
|
||||
quota UserQuota?
|
||||
|
||||
|
@ -103,7 +104,7 @@ enum OAuthProviderType {
|
|||
DISCORD
|
||||
GOOGLE
|
||||
GITHUB
|
||||
AUTHENTIK
|
||||
OIDC
|
||||
}
|
||||
|
||||
model File {
|
||||
|
@ -112,7 +113,7 @@ model File {
|
|||
updatedAt DateTime @updatedAt
|
||||
deletesAt DateTime?
|
||||
|
||||
name String // name & file saved on datasource
|
||||
name String // name & file saved on datasource
|
||||
originalName String? // original name of file when uploaded
|
||||
size BigInt
|
||||
type String
|
||||
|
@ -232,4 +233,4 @@ model Invite {
|
|||
|
||||
inviter User @relation(fields: [inviterId], references: [id], onDelete: Cascade, onUpdate: Cascade)
|
||||
inviterId String
|
||||
}
|
||||
}
|
|
@ -24,14 +24,14 @@ const icons = {
|
|||
DISCORD: <IconBrandDiscordFilled size='1rem' />,
|
||||
GITHUB: <IconBrandGithubFilled size='1rem' />,
|
||||
GOOGLE: <IconBrandGoogleFilled size='1rem' stroke={4} />,
|
||||
AUTHENTIK: <IconCircleKeyFilled size='1rem' />,
|
||||
OIDC: <IconCircleKeyFilled size='1rem' />,
|
||||
};
|
||||
|
||||
const names = {
|
||||
DISCORD: 'Discord',
|
||||
GITHUB: 'GitHub',
|
||||
GOOGLE: 'Google',
|
||||
AUTHENTIK: 'Authentik',
|
||||
OIDC: 'OpenID Connect',
|
||||
};
|
||||
|
||||
function OAuthButton({ provider, linked }: { provider: OAuthProviderType; linked: boolean }) {
|
||||
|
@ -90,7 +90,7 @@ export default function SettingsOAuth() {
|
|||
const discordLinked = findProvider('DISCORD', user?.oauthProviders ?? []);
|
||||
const githubLinked = findProvider('GITHUB', user?.oauthProviders ?? []);
|
||||
const googleLinked = findProvider('GOOGLE', user?.oauthProviders ?? []);
|
||||
const authentikLinked = findProvider('AUTHENTIK', user?.oauthProviders ?? []);
|
||||
const oidcLinked = findProvider('OIDC', user?.oauthProviders ?? []);
|
||||
|
||||
return (
|
||||
<Paper withBorder p='sm'>
|
||||
|
@ -103,7 +103,7 @@ export default function SettingsOAuth() {
|
|||
{config.oauthEnabled.discord && <OAuthButton provider='DISCORD' linked={!!discordLinked} />}
|
||||
{config.oauthEnabled.github && <OAuthButton provider='GITHUB' linked={!!githubLinked} />}
|
||||
{config.oauthEnabled.google && <OAuthButton provider='GOOGLE' linked={!!googleLinked} />}
|
||||
{config.oauthEnabled.authentik && <OAuthButton provider='AUTHENTIK' linked={!!authentikLinked} />}
|
||||
{config.oauthEnabled.oidc && <OAuthButton provider='OIDC' linked={!!oidcLinked} />}
|
||||
</Group>
|
||||
</Paper>
|
||||
);
|
||||
|
|
|
@ -102,7 +102,7 @@ export const rawConfig: any = {
|
|||
clientId: undefined,
|
||||
clientSecret: undefined,
|
||||
},
|
||||
authentik: {
|
||||
oidc: {
|
||||
clientId: undefined,
|
||||
clientSecret: undefined,
|
||||
authorizeUrl: undefined,
|
||||
|
@ -211,11 +211,11 @@ export const PROP_TO_ENV = {
|
|||
'oauth.github.clientSecret': 'OAUTH_GITHUB_CLIENT_SECRET',
|
||||
'oauth.google.clientId': 'OAUTH_GOOGLE_CLIENT_ID',
|
||||
'oauth.google.clientSecret': 'OAUTH_GOOGLE_CLIENT_SECRET',
|
||||
'oauth.authentik.clientId': 'OAUTH_AUTHENTIK_CLIENT_ID',
|
||||
'oauth.authentik.clientSecret': 'OAUTH_AUTHENTIK_CLIENT_SECRET',
|
||||
'oauth.authentik.authorizeUrl': 'OAUTH_AUTHENTIK_AUTHORIZE_URL',
|
||||
'oauth.authentik.userinfoUrl': 'OAUTH_AUTHENTIK_USERINFO_URL',
|
||||
'oauth.authentik.tokenUrl': 'OAUTH_AUTHENTIK_TOKEN_URL',
|
||||
'oauth.oidc.clientId': 'OAUTH_OIDC_CLIENT_ID',
|
||||
'oauth.oidc.clientSecret': 'OAUTH_OIDC_CLIENT_SECRET',
|
||||
'oauth.oidc.authorizeUrl': 'OAUTH_OIDC_AUTHORIZE_URL',
|
||||
'oauth.oidc.userinfoUrl': 'OAUTH_OIDC_USERINFO_URL',
|
||||
'oauth.oidc.tokenUrl': 'OAUTH_OIDC_TOKEN_URL',
|
||||
|
||||
'discord.webhookUrl': 'DISCORD_WEBHOOK_URL',
|
||||
'discord.username': 'DISCORD_USERNAME',
|
||||
|
@ -339,11 +339,11 @@ export function readEnv() {
|
|||
env('oauth.github.clientSecret', 'string'),
|
||||
env('oauth.google.clientId', 'string'),
|
||||
env('oauth.google.clientSecret', 'string'),
|
||||
env('oauth.authentik.clientId', 'string'),
|
||||
env('oauth.authentik.clientSecret', 'string'),
|
||||
env('oauth.authentik.authorizeUrl', 'string'),
|
||||
env('oauth.authentik.userinfoUrl', 'string'),
|
||||
env('oauth.authentik.tokenUrl', 'string'),
|
||||
env('oauth.oidc.clientId', 'string'),
|
||||
env('oauth.oidc.clientSecret', 'string'),
|
||||
env('oauth.oidc.authorizeUrl', 'string'),
|
||||
env('oauth.oidc.userinfoUrl', 'string'),
|
||||
env('oauth.oidc.tokenUrl', 'string'),
|
||||
|
||||
env('discord.webhookUrl', 'string'),
|
||||
env('discord.username', 'string'),
|
||||
|
|
|
@ -242,7 +242,7 @@ export const schema = z.object({
|
|||
clientSecret: z.undefined(),
|
||||
}),
|
||||
),
|
||||
authentik: z
|
||||
oidc: z
|
||||
.object({
|
||||
clientId: z.string(),
|
||||
clientSecret: z.string(),
|
||||
|
|
|
@ -9,6 +9,8 @@ export type User = {
|
|||
role: 'USER' | 'ADMIN' | 'SUPERADMIN';
|
||||
view: UserViewSettings;
|
||||
|
||||
sessions: string[];
|
||||
|
||||
oauthProviders: OAuthProvider[];
|
||||
|
||||
totpSecret?: string | null;
|
||||
|
@ -32,6 +34,7 @@ export const userSelect = {
|
|||
totpSecret: true,
|
||||
passkeys: true,
|
||||
quota: true,
|
||||
sessions: true,
|
||||
};
|
||||
|
||||
export type UserViewSettings = z.infer<typeof userViewSchema>;
|
||||
|
|
|
@ -6,6 +6,7 @@ import { User, userSelect } from '../db/models/user';
|
|||
import { NextApiReq, NextApiRes } from '../response';
|
||||
import { Handler } from './combine';
|
||||
import { isAdministrator } from '../role';
|
||||
import { getSession } from '@/server/session';
|
||||
|
||||
export type ZiplineAuthOptions = {
|
||||
administratorOnly?: boolean;
|
||||
|
@ -47,28 +48,19 @@ export function parseUserToken(
|
|||
export function ziplineAuth(options?: ZiplineAuthOptions) {
|
||||
return (handler: Handler) => {
|
||||
return async (req: NextApiReq, res: NextApiRes) => {
|
||||
let rawToken: string | undefined;
|
||||
|
||||
if (req.cookies.zipline_token) rawToken = req.cookies.zipline_token;
|
||||
else if (req.headers.authorization) rawToken = req.headers.authorization;
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-var
|
||||
var token = parseUserToken(rawToken);
|
||||
} catch (e) {
|
||||
return res.unauthorized((e as { error: string }).error);
|
||||
}
|
||||
const session = await getSession(req, res);
|
||||
if (!session.id || !session.sessionId) return res.unauthorized('invalid session, not logged in');
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
token,
|
||||
},
|
||||
select: {
|
||||
...userSelect,
|
||||
...(options?.select && options.select),
|
||||
sessions: {
|
||||
has: session.sessionId,
|
||||
},
|
||||
},
|
||||
select: userSelect,
|
||||
});
|
||||
if (!user) return res.unauthorized();
|
||||
|
||||
if (!user) return res.unauthorized('invalid login session');
|
||||
|
||||
req.user = user;
|
||||
|
||||
|
|
|
@ -20,12 +20,12 @@ export default function enabled(config: Config) {
|
|||
config.features.oauthRegistration,
|
||||
);
|
||||
|
||||
const authentikEnabled = isTruthy(
|
||||
config.oauth?.authentik?.clientId,
|
||||
config.oauth?.authentik?.clientSecret,
|
||||
config.oauth?.authentik?.authorizeUrl,
|
||||
config.oauth?.authentik?.tokenUrl,
|
||||
config.oauth?.authentik?.userinfoUrl,
|
||||
const oidcEnabled = isTruthy(
|
||||
config.oauth?.oidc?.clientId,
|
||||
config.oauth?.oidc?.clientSecret,
|
||||
config.oauth?.oidc?.authorizeUrl,
|
||||
config.oauth?.oidc?.tokenUrl,
|
||||
config.oauth?.oidc?.userinfoUrl,
|
||||
config.features.oauthRegistration,
|
||||
);
|
||||
|
||||
|
@ -33,6 +33,6 @@ export default function enabled(config: Config) {
|
|||
discord: discordEnabled,
|
||||
github: githubEnabled,
|
||||
google: googleEnabled,
|
||||
authentik: authentikEnabled,
|
||||
oidc: oidcEnabled,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -61,10 +61,10 @@ export const googleAuth = {
|
|||
},
|
||||
};
|
||||
|
||||
export const authentikAuth = {
|
||||
export const oidcAuth = {
|
||||
url: (clientId: string, origin: string, authorizeUrl: string, state?: string) =>
|
||||
`${authorizeUrl}?client_id=${clientId}&redirect_uri=${encodeURIComponent(
|
||||
`${origin}/api/auth/oauth/authentik`,
|
||||
`${origin}/api/auth/oauth/oidc`,
|
||||
)}&response_type=code&scope=openid+email+profile+offline_access${state ? `&state=${state}` : ''}`,
|
||||
user: async (accessToken: string, userInfoUrl: string) => {
|
||||
const res = await fetch(userInfoUrl, {
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import { NextApiReq, NextApiRes } from '@/lib/response';
|
||||
import { OAuthProviderType } from '@prisma/client';
|
||||
import { prisma } from '../db';
|
||||
import { parseUserToken } from '../middleware/ziplineAuth';
|
||||
import { findProvider } from './providerUtil';
|
||||
import { createToken } from '../crypto';
|
||||
import { config } from '../config';
|
||||
import { User } from '../db/models/user';
|
||||
import Logger, { log } from '../logger';
|
||||
import { getSession } from '@/server/session';
|
||||
import { getSession, saveSession } from '@/server/session';
|
||||
|
||||
export interface OAuthQuery {
|
||||
state?: string;
|
||||
|
@ -76,15 +75,11 @@ export const withOAuth =
|
|||
|
||||
const { state } = req.query as OAuthQuery;
|
||||
|
||||
let rawToken: string | undefined;
|
||||
|
||||
if (req.cookies.zipline_token) rawToken = req.cookies.zipline_token;
|
||||
else if (req.headers.authorization) rawToken = req.headers.authorization;
|
||||
const token = parseUserToken(rawToken, true);
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
token: token ?? '',
|
||||
sessions: {
|
||||
has: session.sessionId ?? '',
|
||||
},
|
||||
},
|
||||
include: {
|
||||
oauthProviders: true,
|
||||
|
@ -94,7 +89,7 @@ export const withOAuth =
|
|||
const userOauth = findProvider(provider, user?.oauthProviders ?? []);
|
||||
|
||||
if (state === 'link') {
|
||||
if (!user) return res.unauthorized();
|
||||
if (!user) return res.unauthorized('invalid session');
|
||||
|
||||
if (findProvider(provider, user.oauthProviders))
|
||||
return res.badRequest('This account is already linked to this provider');
|
||||
|
@ -122,8 +117,7 @@ export const withOAuth =
|
|||
},
|
||||
});
|
||||
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, <User>user);
|
||||
|
||||
logger.info('linked oauth account', {
|
||||
provider,
|
||||
|
@ -153,8 +147,7 @@ export const withOAuth =
|
|||
},
|
||||
});
|
||||
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, <User>user);
|
||||
|
||||
return res.redirect('/dashboard');
|
||||
} else if (existingOauth) {
|
||||
|
@ -173,8 +166,7 @@ export const withOAuth =
|
|||
},
|
||||
});
|
||||
|
||||
session.user = login.user! as User;
|
||||
await session.save();
|
||||
await saveSession(session, <User>login.user!);
|
||||
|
||||
logger.info('logged in with oauth', {
|
||||
provider,
|
||||
|
@ -206,8 +198,7 @@ export const withOAuth =
|
|||
},
|
||||
});
|
||||
|
||||
session.user = nuser as User;
|
||||
await session.save();
|
||||
await saveSession(session, <User>nuser);
|
||||
|
||||
logger.info('created user with oauth', {
|
||||
provider,
|
||||
|
|
|
@ -69,7 +69,7 @@ export async function handleOverrideColors(theme: ZiplineTheme) {
|
|||
...theme.colors,
|
||||
google: theme.colors?.google || Array(10).fill('#4285F4'),
|
||||
github: theme.colors?.github || Array(10).fill('#24292E'),
|
||||
authentik: theme.colors?.authentik || Array(10).fill('#FD4B2D'),
|
||||
oidc: theme.colors?.oidc || Array(10).fill('#72abcf'),
|
||||
discord: theme.colors?.discord || Array(10).fill('#5865F2'),
|
||||
},
|
||||
} as ZiplineTheme;
|
||||
|
|
26
src/pages/api/auth/oauth/authentik.ts → src/pages/api/auth/oauth/oidc.ts
Executable file → Normal file
26
src/pages/api/auth/oauth/authentik.ts → src/pages/api/auth/oauth/oidc.ts
Executable file → Normal file
|
@ -3,7 +3,7 @@ import Logger from '@/lib/logger';
|
|||
import { combine } from '@/lib/middleware/combine';
|
||||
import { method } from '@/lib/middleware/method';
|
||||
import enabled from '@/lib/oauth/enabled';
|
||||
import { authentikAuth } from '@/lib/oauth/providerUtil';
|
||||
import { oidcAuth } from '@/lib/oauth/providerUtil';
|
||||
import { OAuthQuery, OAuthResponse, withOAuth } from '@/lib/oauth/withOAuth';
|
||||
|
||||
// thanks to @danejur for this https://github.com/diced/zipline/pull/372
|
||||
|
@ -14,33 +14,33 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
|
|||
error_code: 403,
|
||||
};
|
||||
|
||||
const { authentik: authentikEnabled } = enabled(config);
|
||||
const { oidc: oidcEnabled } = enabled(config);
|
||||
|
||||
if (!authentikEnabled)
|
||||
if (!oidcEnabled)
|
||||
return {
|
||||
error: 'Authentik OAuth is not configured.',
|
||||
error: 'OpenID Connect OAuth is not configured.',
|
||||
error_code: 401,
|
||||
};
|
||||
|
||||
if (!code)
|
||||
return {
|
||||
redirect: authentikAuth.url(
|
||||
config.oauth.authentik.clientId!,
|
||||
redirect: oidcAuth.url(
|
||||
config.oauth.oidc.clientId!,
|
||||
`${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}`,
|
||||
config.oauth.authentik.authorizeUrl!,
|
||||
config.oauth.oidc.authorizeUrl!,
|
||||
state,
|
||||
),
|
||||
};
|
||||
|
||||
const body = new URLSearchParams({
|
||||
client_id: config.oauth.authentik.clientId!,
|
||||
client_secret: config.oauth.authentik.clientSecret!,
|
||||
client_id: config.oauth.oidc.clientId!,
|
||||
client_secret: config.oauth.oidc.clientSecret!,
|
||||
grant_type: 'authorization_code',
|
||||
code,
|
||||
redirect_uri: `${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}/api/auth/oauth/authentik`,
|
||||
redirect_uri: `${config.core.returnHttpsUrls ? 'https' : 'http'}://${host}/api/auth/oauth/oidc`,
|
||||
});
|
||||
|
||||
const res = await fetch(config.oauth.authentik.tokenUrl!, {
|
||||
const res = await fetch(config.oauth.oidc.tokenUrl!, {
|
||||
method: 'POST',
|
||||
body,
|
||||
headers: {
|
||||
|
@ -57,7 +57,7 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
|
|||
if (!json.access_token) return { error: 'No access token in response' };
|
||||
if (!json.refresh_token) return { error: 'No refresh token in response' };
|
||||
|
||||
const userJson = await authentikAuth.user(json.access_token, config.oauth.authentik.userinfoUrl!);
|
||||
const userJson = await oidcAuth.user(json.access_token, config.oauth.oidc.userinfoUrl!);
|
||||
if (!userJson) return { error: 'Failed to fetch user' };
|
||||
|
||||
return {
|
||||
|
@ -68,4 +68,4 @@ async function handler({ code, state, host }: OAuthQuery, _logger: Logger): Prom
|
|||
};
|
||||
}
|
||||
|
||||
export default combine([method(['GET'])], withOAuth('AUTHENTIK', handler));
|
||||
export default combine([method(['GET'])], withOAuth('OIDC', handler));
|
|
@ -332,8 +332,8 @@ export default function Login({ config }: InferGetServerSidePropsType<typeof get
|
|||
leftSection={<IconBrandGoogleFilled stroke={4} />}
|
||||
/>
|
||||
)}
|
||||
{config.oauthEnabled.authentik && (
|
||||
<ExternalAuthButton provider='Authentik' alpha={0.2} leftSection={<IconCircleKeyFilled />} />
|
||||
{config.oauthEnabled.oidc && (
|
||||
<ExternalAuthButton provider='OIDC' alpha={0.2} leftSection={<IconCircleKeyFilled />} />
|
||||
)}
|
||||
</Stack>
|
||||
</Card>
|
||||
|
|
|
@ -41,11 +41,13 @@ export function parseUserToken(
|
|||
export async function userMiddleware(req: FastifyRequest, res: FastifyReply) {
|
||||
const session = await getSession(req, res);
|
||||
|
||||
if (!session.user) return res.unauthorized('not logged in');
|
||||
if (!session.id || !session.sessionId) return res.unauthorized('not logged in');
|
||||
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
password: session.user.password,
|
||||
sessions: {
|
||||
has: session.sessionId,
|
||||
},
|
||||
},
|
||||
select: userSelect,
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import { verifyPassword } from '@/lib/crypto';
|
|||
import { prisma } from '@/lib/db';
|
||||
import { User, userSelect } from '@/lib/db/models/user';
|
||||
import { verifyTotpCode } from '@/lib/totp';
|
||||
import { getSession } from '@/server/session';
|
||||
import { getSession, saveSession } from '@/server/session';
|
||||
import fastifyPlugin from 'fastify-plugin';
|
||||
|
||||
export type ApiLoginResponse = {
|
||||
|
@ -27,7 +27,8 @@ export default fastifyPlugin(
|
|||
handler: async (req, res) => {
|
||||
const session = await getSession(req, res);
|
||||
|
||||
session.user = null;
|
||||
session.id = null;
|
||||
session.sessionId = null;
|
||||
|
||||
const { username, password, code } = req.body;
|
||||
|
||||
|
@ -60,8 +61,7 @@ export default fastifyPlugin(
|
|||
totp: true,
|
||||
});
|
||||
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, user as User, false);
|
||||
|
||||
delete (user as any).password;
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { config } from '@/lib/config';
|
||||
import { createToken, hashPassword } from '@/lib/crypto';
|
||||
import { prisma } from '@/lib/db';
|
||||
import { userSelect } from '@/lib/db/models/user';
|
||||
import { getSession } from '@/server/session';
|
||||
import { User, userSelect } from '@/lib/db/models/user';
|
||||
import { getSession, saveSession } from '@/server/session';
|
||||
import fastifyPlugin from 'fastify-plugin';
|
||||
import { ApiLoginResponse } from './login';
|
||||
|
||||
|
@ -76,8 +76,7 @@ export default fastifyPlugin(
|
|||
},
|
||||
});
|
||||
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, <User>user);
|
||||
|
||||
delete (user as any).password;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { config } from '@/lib/config';
|
||||
import { prisma } from '@/lib/db';
|
||||
import { User, userSelect } from '@/lib/db/models/user';
|
||||
import { getSession } from '@/server/session';
|
||||
import { getSession, saveSession } from '@/server/session';
|
||||
import { AuthenticationResponseJSON } from '@github/webauthn-json/dist/types/browser-ponyfill';
|
||||
import fastifyPlugin from 'fastify-plugin';
|
||||
|
||||
|
@ -47,8 +47,7 @@ export default fastifyPlugin(
|
|||
});
|
||||
if (!user) return res.badRequest('Invalid passkey');
|
||||
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, <User>user);
|
||||
|
||||
delete (user as any).password;
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { hashPassword } from '@/lib/crypto';
|
|||
import { prisma } from '@/lib/db';
|
||||
import { User, userSelect } from '@/lib/db/models/user';
|
||||
import { userMiddleware } from '@/server/middleware/user';
|
||||
import { getSession } from '@/server/session';
|
||||
import { getSession, saveSession } from '@/server/session';
|
||||
import fastifyPlugin from 'fastify-plugin';
|
||||
|
||||
export type ApiUserResponse = {
|
||||
|
@ -75,12 +75,13 @@ export default fastifyPlugin(
|
|||
},
|
||||
select: {
|
||||
...userSelect,
|
||||
password: true,
|
||||
token: true,
|
||||
},
|
||||
});
|
||||
|
||||
const session = await getSession(req, res);
|
||||
session.user = user;
|
||||
await session.save();
|
||||
await saveSession(session, <User>user);
|
||||
|
||||
delete (user as any).password;
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import { createToken, encryptToken } from '@/lib/crypto';
|
|||
import { prisma } from '@/lib/db';
|
||||
import { User, userSelect } from '@/lib/db/models/user';
|
||||
import { userMiddleware } from '@/server/middleware/user';
|
||||
import { getSession } from '@/server/session';
|
||||
import fastifyPlugin from 'fastify-plugin';
|
||||
|
||||
export type ApiUserTokenResponse = {
|
||||
|
@ -32,8 +31,6 @@ export default fastifyPlugin(
|
|||
});
|
||||
|
||||
server.patch(PATH, { preHandler: [userMiddleware] }, async (req, res) => {
|
||||
const session = await getSession(req, res);
|
||||
|
||||
const user = await prisma.user.update({
|
||||
where: {
|
||||
id: req.user.id,
|
||||
|
@ -47,8 +44,6 @@ export default fastifyPlugin(
|
|||
},
|
||||
});
|
||||
|
||||
session.user!.token = user.token;
|
||||
|
||||
delete (user as any).password;
|
||||
|
||||
return res.send({
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { config } from '@/lib/config';
|
||||
import { prisma } from '@/lib/db';
|
||||
import { User } from '@/lib/db/models/user';
|
||||
import { randomCharacters } from '@/lib/random';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
import { IncomingMessage, ServerResponse } from 'http';
|
||||
import { getIronSession } from 'iron-session';
|
||||
|
@ -12,12 +14,17 @@ const cookieOptions = {
|
|||
sameSite: 'lax',
|
||||
};
|
||||
|
||||
export type ZiplineSession = {
|
||||
id: string | null;
|
||||
sessionId: string | null;
|
||||
};
|
||||
|
||||
export async function getSession(
|
||||
req: FastifyRequest | IncomingMessage,
|
||||
reply: FastifyReply | ServerResponse<IncomingMessage>,
|
||||
) {
|
||||
if (!(req as any).raw || !(req as any).raw) {
|
||||
const session = await getIronSession<{ user: User | null }>(
|
||||
const session = await getIronSession<ZiplineSession>(
|
||||
req as IncomingMessage,
|
||||
reply as ServerResponse<IncomingMessage>,
|
||||
{
|
||||
|
@ -30,7 +37,7 @@ export async function getSession(
|
|||
return session;
|
||||
}
|
||||
|
||||
const session = await getIronSession<{ user: User | null }>(
|
||||
const session = await getIronSession<ZiplineSession>(
|
||||
(req as FastifyRequest).raw,
|
||||
(reply as FastifyReply).raw,
|
||||
{
|
||||
|
@ -42,3 +49,25 @@ export async function getSession(
|
|||
|
||||
return session;
|
||||
}
|
||||
|
||||
export async function saveSession(
|
||||
session: Awaited<ReturnType<typeof getSession>>,
|
||||
user: User,
|
||||
overwriteSessions = true,
|
||||
) {
|
||||
session.id = user.id;
|
||||
|
||||
const sessionId = randomCharacters(32);
|
||||
session.sessionId = sessionId;
|
||||
|
||||
await prisma.user.update({
|
||||
where: {
|
||||
id: user.id,
|
||||
},
|
||||
data: {
|
||||
sessions: overwriteSessions ? { set: [sessionId] } : { push: sessionId },
|
||||
},
|
||||
});
|
||||
|
||||
await session.save();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue