mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Creating session cookie util, typing cookies, and fixing session creation and cookie creation for flows.
This commit is contained in:
parent
50cd97993a
commit
7d4b1a8eb9
9 changed files with 72 additions and 100 deletions
|
|
@ -93,13 +93,13 @@
|
|||
"@oslojs/otp": "^1.0.0",
|
||||
"@oslojs/webauthn": "^1.0.0",
|
||||
"@paralleldrive/cuid2": "^2.2.2",
|
||||
"@scalar/hono-api-reference": "^0.5.158",
|
||||
"@scalar/hono-api-reference": "^0.5.159",
|
||||
"@sveltejs/adapter-node": "^5.2.9",
|
||||
"@sveltejs/adapter-vercel": "^5.4.7",
|
||||
"@types/feather-icons": "^4.29.4",
|
||||
"bits-ui": "^0.21.16",
|
||||
"boardgamegeekclient": "^1.9.1",
|
||||
"bullmq": "^5.25.1",
|
||||
"bullmq": "^5.25.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
"cookie": "^1.0.1",
|
||||
|
|
|
|||
|
|
@ -66,8 +66,8 @@ importers:
|
|||
specifier: ^2.2.2
|
||||
version: 2.2.2
|
||||
'@scalar/hono-api-reference':
|
||||
specifier: ^0.5.158
|
||||
version: 0.5.158(hono@4.6.9)
|
||||
specifier: ^0.5.159
|
||||
version: 0.5.159(hono@4.6.9)
|
||||
'@sveltejs/adapter-node':
|
||||
specifier: ^5.2.9
|
||||
version: 5.2.9(@sveltejs/kit@2.8.0(@sveltejs/vite-plugin-svelte@4.0.0-next.7(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))(svelte@5.0.0-next.175)(vite@5.4.10(@types/node@20.17.6)))
|
||||
|
|
@ -84,8 +84,8 @@ importers:
|
|||
specifier: ^1.9.1
|
||||
version: 1.9.1
|
||||
bullmq:
|
||||
specifier: ^5.25.1
|
||||
version: 5.25.1
|
||||
specifier: ^5.25.3
|
||||
version: 5.25.3
|
||||
class-variance-authority:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
|
|
@ -2156,18 +2156,18 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@scalar/hono-api-reference@0.5.158':
|
||||
resolution: {integrity: sha512-2P7l/ivuC/RWpAKddLtkqIZ89TA5/QlpfEBrdpH9/yjn4NpR5XkbtT+/8uZVELCyjfUMpGcaNXrxuJERFJ3sxA==}
|
||||
'@scalar/hono-api-reference@0.5.159':
|
||||
resolution: {integrity: sha512-nUKaN0CKvytbXPj9b6taF/efKKRqEUwhVxlfLVjrJXN0eHNHDWxG9e/5Tyw1o2MXJo1cQpGZ4qTh48k/8u6ZjA==}
|
||||
engines: {node: '>=18'}
|
||||
peerDependencies:
|
||||
hono: ^4.0.0
|
||||
|
||||
'@scalar/openapi-types@0.1.4':
|
||||
resolution: {integrity: sha512-+wRXgmqzgDnj8Dxqf4OOPMPo4or/LRd1Bsy4pnrIW0yBt8rKSdtBb+jH/aRnhgDDmKVjWxJ+KFk7WlSKvZwNTw==}
|
||||
'@scalar/openapi-types@0.1.5':
|
||||
resolution: {integrity: sha512-6geH9ehvQ/sG/xUyy3e0lyOw3BaY5s6nn22wHjEJhcobdmWyFER0O6m7AU0ZN4QTjle/gYvFJOjj552l/rsNSw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@scalar/types@0.0.18':
|
||||
resolution: {integrity: sha512-gfJB/e9Rq/vjsiWlNwBkaIAZVb9v5guHQB5uVoVFcU0gdAuXni0KVxFxl3gGTu2zhBdB+DkixjyPcNzpqwksmA==}
|
||||
'@scalar/types@0.0.19':
|
||||
resolution: {integrity: sha512-wOxtXd35BS0DaVhBopQUB8c8hfLQ+/PKEr99GbOZW+4DWCrEB8JfWJgvpJyxHU6by7LHNVY4fvpFQR7Ezh1IIw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
'@sideway/address@4.1.5':
|
||||
|
|
@ -2567,8 +2567,8 @@ packages:
|
|||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
bullmq@5.25.1:
|
||||
resolution: {integrity: sha512-3q87TXIN31OgNeHT9n4nQWVV5wMog6dXUAoz1rAMXjb1QT2Fn8pElg5m/IHomqe7Q7FA/3b/8q4W8RCAFFUmRA==}
|
||||
bullmq@5.25.3:
|
||||
resolution: {integrity: sha512-nUFTszxV/V3qJMZQxSMNOBF1HiGKh895WyJmE5keUonkutpTsxdYIr0dzVUTPbhXvBvW9LWlY7BetWY3afy/MQ==}
|
||||
|
||||
bytes@3.1.2:
|
||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||
|
|
@ -6560,16 +6560,16 @@ snapshots:
|
|||
'@rollup/rollup-win32-x64-msvc@4.24.0':
|
||||
optional: true
|
||||
|
||||
'@scalar/hono-api-reference@0.5.158(hono@4.6.9)':
|
||||
'@scalar/hono-api-reference@0.5.159(hono@4.6.9)':
|
||||
dependencies:
|
||||
'@scalar/types': 0.0.18
|
||||
'@scalar/types': 0.0.19
|
||||
hono: 4.6.9
|
||||
|
||||
'@scalar/openapi-types@0.1.4': {}
|
||||
'@scalar/openapi-types@0.1.5': {}
|
||||
|
||||
'@scalar/types@0.0.18':
|
||||
'@scalar/types@0.0.19':
|
||||
dependencies:
|
||||
'@scalar/openapi-types': 0.1.4
|
||||
'@scalar/openapi-types': 0.1.5
|
||||
'@unhead/schema': 1.11.11
|
||||
|
||||
'@sideway/address@4.1.5':
|
||||
|
|
@ -7061,7 +7061,7 @@ snapshots:
|
|||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
bullmq@5.25.1:
|
||||
bullmq@5.25.3:
|
||||
dependencies:
|
||||
cron-parser: 4.9.0
|
||||
ioredis: 5.4.1
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { config } from '$lib/server/api/common/config';
|
||||
import env from '$lib/server/api/common/env';
|
||||
import type { Context } from 'hono';
|
||||
import { setCookie } from 'hono/cookie';
|
||||
import type { CookieOptions } from 'hono/utils/cookie';
|
||||
import { TimeSpan } from 'oslo';
|
||||
|
||||
export const cookieMaxAge = 60 * 60 * 24 * 30;
|
||||
|
|
@ -9,7 +12,13 @@ export const halfCookieExpiresMilliseconds = cookieExpiresMilliseconds / 2;
|
|||
export const halfCookieExpiresAt = new Date(Date.now() + halfCookieExpiresMilliseconds);
|
||||
export const cookieName = 'session';
|
||||
|
||||
export function createSessionTokenCookie(token: string, expiresAt: Date) {
|
||||
export type SessionCookie = {
|
||||
name: string;
|
||||
value: string;
|
||||
attributes: CookieOptions;
|
||||
};
|
||||
|
||||
export function createSessionTokenCookie(token: string, expiresAt: Date): SessionCookie {
|
||||
return {
|
||||
name: cookieName,
|
||||
value: token,
|
||||
|
|
@ -25,7 +34,7 @@ export function createSessionTokenCookie(token: string, expiresAt: Date) {
|
|||
};
|
||||
}
|
||||
|
||||
export function createBlankSessionTokenCookie() {
|
||||
export function createBlankSessionTokenCookie(): SessionCookie {
|
||||
return {
|
||||
name: cookieName,
|
||||
value: '',
|
||||
|
|
@ -40,3 +49,15 @@ export function createBlankSessionTokenCookie() {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function setSessionCookie(c: Context, sessionCookie: SessionCookie) {
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge: sessionCookie.attributes?.maxAge,
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as undefined,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { StatusCodes } from '$lib/constants/status-codes';
|
||||
import { Controller } from '$lib/server/api/common/types/controller';
|
||||
import { createBlankSessionTokenCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { createBlankSessionTokenCookie, setSessionCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { changePasswordDto } from '$lib/server/api/dtos/change-password.dto';
|
||||
import { updateEmailDto } from '$lib/server/api/dtos/update-email.dto';
|
||||
import { updateProfileDto } from '$lib/server/api/dtos/update-profile.dto';
|
||||
|
|
@ -81,7 +81,8 @@ export class IamController extends Controller {
|
|||
await this.iamService.updatePassword(user.id, { password, confirm_password });
|
||||
await this.sessionsService.invalidateSession(user.id);
|
||||
await this.loginRequestService.createUserSession(user.id, c.req, undefined);
|
||||
deleteSessionTokenCookie(c);
|
||||
const sessionCookie = createBlankSessionTokenCookie();
|
||||
setSessionCookie(c, sessionCookie);
|
||||
return c.json({ status: 'success' });
|
||||
} catch (error) {
|
||||
console.error('Error updating password', error);
|
||||
|
|
@ -108,7 +109,8 @@ export class IamController extends Controller {
|
|||
.post('/logout', requireAuth, openApi(logout), async (c) => {
|
||||
const sessionId = c.var.session.id;
|
||||
await this.iamService.logout(sessionId);
|
||||
deleteSessionTokenCookie(c);
|
||||
const sessionCookie = createBlankSessionTokenCookie();
|
||||
setSessionCookie(c, sessionCookie);
|
||||
return c.json({ status: 'success' });
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
import 'reflect-metadata';
|
||||
import { Controller } from '$lib/server/api/common/types/controller';
|
||||
import { cookieExpiresAt, createSessionTokenCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { cookieExpiresAt, createSessionTokenCookie, setSessionCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { signinUsernameDto } from '$lib/server/api/dtos/signin-username.dto';
|
||||
import { SessionsService } from '$lib/server/api/services/sessions.service';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { openApi } from 'hono-zod-openapi';
|
||||
import { setCookie } from 'hono/cookie';
|
||||
import { TimeSpan } from 'oslo';
|
||||
import { inject, injectable } from 'tsyringe';
|
||||
import { limiter } from '../middleware/rate-limiter.middleware';
|
||||
import { LoginRequestsService } from '../services/loginrequest.service';
|
||||
|
|
@ -32,18 +30,7 @@ export class LoginController extends Controller {
|
|||
const session = await this.loginRequestsService.verify({ username, password }, c.req);
|
||||
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
console.log('set cookie', sessionCookie);
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
setSessionCookie(c, sessionCookie);
|
||||
return c.json({ message: 'ok' });
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import 'reflect-metadata';
|
||||
import { Controller } from '$lib/server/api/common/types/controller';
|
||||
import type { OAuthUser } from '$lib/server/api/common/types/oauth';
|
||||
import { createSessionTokenCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { cookieExpiresAt, createSessionTokenCookie, setSessionCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import { OAuthService } from '$lib/server/api/services/oauth.service';
|
||||
import { SessionsService } from '$lib/server/api/services/sessions.service';
|
||||
import { github, google } from '$lib/server/auth';
|
||||
|
|
@ -50,21 +50,8 @@ export class OAuthController extends Controller {
|
|||
|
||||
const sessionToken = this.sessionsService.generateSessionToken();
|
||||
const session = await this.sessionsService.createSession(sessionToken, userId, '', '', false, false);
|
||||
const sessionCookie = createSessionTokenCookie(session.id, new Date(new TimeSpan(2, 'w').milliseconds()));
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
|
||||
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
setSessionCookie(c, sessionCookie);
|
||||
return c.json({ message: 'ok' });
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
|
@ -106,22 +93,10 @@ export class OAuthController extends Controller {
|
|||
};
|
||||
|
||||
const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'google');
|
||||
|
||||
const session = await this.sessionsService.createSession();
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id);
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
const sessionToken = this.sessionsService.generateSessionToken();
|
||||
const session = await this.sessionsService.createSession(sessionToken, userId, '', '', false, false);
|
||||
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
setSessionCookie(c, sessionCookie);
|
||||
|
||||
return c.json({ message: 'ok' });
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
import 'reflect-metadata';
|
||||
import { cookieExpiresAt, cookieName, createBlankSessionTokenCookie, createSessionTokenCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import {
|
||||
type SessionCookie,
|
||||
cookieExpiresAt,
|
||||
cookieName,
|
||||
createBlankSessionTokenCookie,
|
||||
createSessionTokenCookie,
|
||||
setSessionCookie,
|
||||
} from '$lib/server/api/common/utils/cookies';
|
||||
import { SessionsService } from '$lib/server/api/services/sessions.service';
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import { setCookie } from 'hono/cookie';
|
||||
import { createMiddleware } from 'hono/factory';
|
||||
import { TimeSpan } from 'oslo';
|
||||
import { parseCookies } from 'oslo/cookie';
|
||||
import { verifyRequestOrigin } from 'oslo/request';
|
||||
import { container } from 'tsyringe';
|
||||
|
|
@ -35,32 +40,13 @@ export const validateAuthSession: MiddlewareHandler<AppBindings> = createMiddlew
|
|||
}
|
||||
|
||||
const { session, user } = await sessionService.validateSessionToken(sessionId);
|
||||
let sessionCookie: SessionCookie;
|
||||
if (session !== null) {
|
||||
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge:
|
||||
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
|
||||
? sessionCookie.attributes.maxAge
|
||||
: new TimeSpan(2, 'w').seconds(),
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
} else {
|
||||
const sessionCookie = createBlankSessionTokenCookie();
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
maxAge: sessionCookie.attributes?.maxAge,
|
||||
domain: sessionCookie.attributes.domain,
|
||||
sameSite: sessionCookie.attributes.sameSite as any,
|
||||
secure: sessionCookie.attributes.secure,
|
||||
httpOnly: sessionCookie.attributes.httpOnly,
|
||||
expires: sessionCookie.attributes.expires,
|
||||
});
|
||||
sessionCookie = createBlankSessionTokenCookie();
|
||||
}
|
||||
setSessionCookie(c, sessionCookie);
|
||||
c.set('session', session);
|
||||
c.set('user', user);
|
||||
return next();
|
||||
|
|
|
|||
|
|
@ -27,12 +27,12 @@ simple as possible. This makes the service easier to read, test and understand.
|
|||
@injectable()
|
||||
export class IamService {
|
||||
constructor(
|
||||
@inject(SessionsService) private luciaService: SessionsService,
|
||||
@inject(SessionsService) private sessionsService: SessionsService,
|
||||
@inject(UsersService) private readonly usersService: UsersService,
|
||||
) {}
|
||||
|
||||
async logout(sessionId: string) {
|
||||
return this.luciaService.lucia.invalidateSession(sessionId);
|
||||
return this.sessionsService.invalidateSession(sessionId);
|
||||
}
|
||||
|
||||
async updateProfile(userId: string, data: UpdateProfileDto) {
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ export class SessionsService {
|
|||
}
|
||||
|
||||
async validateSessionToken(token: string): Promise<SessionValidationResult> {
|
||||
// TODO: Why was this needed in the docs? https://lucia-next.pages.dev/sessions/basic-api/drizzle-orm
|
||||
// const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
||||
const sessions = await this.sessionsRepository.findBySessionId(token);
|
||||
if (sessions.length < 1) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue