mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Fixing some auth implementations. Removed text encoder for validate session token, unsure why it is needed.
This commit is contained in:
parent
faaf287bf0
commit
50cd97993a
8 changed files with 75 additions and 39 deletions
|
|
@ -1,7 +1,8 @@
|
|||
import type { Sessions } from '$lib/server/api/databases/postgres/tables';
|
||||
import type { Hono } from 'hono';
|
||||
import type { PinoLogger } from 'hono-pino';
|
||||
import type { Promisify, RateLimitInfo } from 'hono-rate-limiter';
|
||||
import type { Session, User } from 'lucia';
|
||||
import type { User } from 'lucia';
|
||||
|
||||
// export type AppOpenAPI = OpenAPIHono<AppBindings>;
|
||||
export type AppOpenAPI = Hono<AppBindings>;
|
||||
|
|
@ -9,7 +10,7 @@ export type AppOpenAPI = Hono<AppBindings>;
|
|||
export type AppBindings = {
|
||||
Variables: {
|
||||
logger: PinoLogger;
|
||||
session: Session | null;
|
||||
session: Sessions | null;
|
||||
user: User | null;
|
||||
rateLimit: RateLimitInfo;
|
||||
rateLimitStore: {
|
||||
|
|
@ -22,7 +23,7 @@ export type AppBindings = {
|
|||
export type HonoTypes = {
|
||||
Variables: {
|
||||
logger: PinoLogger;
|
||||
session: Session | null;
|
||||
session: Sessions | null;
|
||||
user: User | null;
|
||||
rateLimit: RateLimitInfo;
|
||||
rateLimitStore: {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,21 @@
|
|||
import { config } from '$lib/server/api/common/config';
|
||||
import env from '$lib/server/api/common/env';
|
||||
import { TimeSpan } from 'oslo';
|
||||
|
||||
export const cookieMaxAge = 60 * 60 * 24 * 30;
|
||||
export const cookieExpiresMilliseconds = new TimeSpan(2, 'w').milliseconds();
|
||||
export const cookieExpiresAt = new Date(Date.now() + cookieExpiresMilliseconds);
|
||||
export const halfCookieExpiresMilliseconds = cookieExpiresMilliseconds / 2;
|
||||
export const halfCookieExpiresAt = new Date(Date.now() + halfCookieExpiresMilliseconds);
|
||||
export const cookieName = 'session';
|
||||
|
||||
export function createSessionTokenCookie(token: string, expiresAt: Date) {
|
||||
return {
|
||||
name: 'session',
|
||||
name: cookieName,
|
||||
value: token,
|
||||
attributes: {
|
||||
path: '/',
|
||||
maxAge: 60 * 60 * 24 * 30,
|
||||
maxAge: cookieMaxAge,
|
||||
domain: env.DOMAIN,
|
||||
sameSite: 'lax',
|
||||
secure: config.isProduction,
|
||||
|
|
@ -19,7 +27,7 @@ export function createSessionTokenCookie(token: string, expiresAt: Date) {
|
|||
|
||||
export function createBlankSessionTokenCookie() {
|
||||
return {
|
||||
name: 'session',
|
||||
name: cookieName,
|
||||
value: '',
|
||||
attributes: {
|
||||
path: '/',
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'reflect-metadata';
|
||||
import { Controller } from '$lib/server/api/common/types/controller';
|
||||
import { cookieExpiresAt, createSessionTokenCookie } 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';
|
||||
|
|
@ -29,7 +30,7 @@ export class LoginController extends Controller {
|
|||
async (c) => {
|
||||
const { username, password } = c.req.valid('json');
|
||||
const session = await this.loginRequestsService.verify({ username, password }, c.req);
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id);
|
||||
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
|
||||
console.log('set cookie', sessionCookie);
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
path: sessionCookie.attributes.path,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import { github, google } from '$lib/server/auth';
|
|||
import { OAuth2RequestError } from 'arctic';
|
||||
import { getCookie, setCookie } from 'hono/cookie';
|
||||
import { TimeSpan } from 'oslo';
|
||||
|
||||
import { inject, injectable } from 'tsyringe';
|
||||
|
||||
@injectable()
|
||||
|
|
@ -48,8 +49,7 @@ export class OAuthController extends Controller {
|
|||
const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'github');
|
||||
|
||||
const sessionToken = this.sessionsService.generateSessionToken();
|
||||
const session = await this.sessionsService.createSession(sessionToken, userId,
|
||||
req.);
|
||||
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, {
|
||||
|
|
@ -107,7 +107,7 @@ export class OAuthController extends Controller {
|
|||
|
||||
const userId = await this.oauthService.handleOAuthUser(oAuthUser, 'google');
|
||||
|
||||
const session = await this.luciaService.lucia.createSession(userId, {});
|
||||
const session = await this.sessionsService.createSession();
|
||||
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id);
|
||||
|
||||
setCookie(c, sessionCookie.name, sessionCookie.value, {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { createSessionTokenCookie } from '$lib/server/api/common/utils/cookies';
|
||||
import 'reflect-metadata';
|
||||
import { cookieExpiresAt, cookieName, createBlankSessionTokenCookie, createSessionTokenCookie } 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';
|
||||
import type { AppBindings } from '../common/types/hono';
|
||||
|
|
@ -22,19 +26,40 @@ export const verifyOrigin: MiddlewareHandler<AppBindings> = createMiddleware(asy
|
|||
});
|
||||
|
||||
export const validateAuthSession: MiddlewareHandler<AppBindings> = createMiddleware(async (c, next) => {
|
||||
const sessionId = lucia.readSessionCookie(c.req.header('Cookie') ?? '');
|
||||
const cookies = parseCookies(c.req.header('Cookie') ?? '');
|
||||
const sessionId = cookies.get(cookieName) ?? null;
|
||||
if (!sessionId) {
|
||||
c.set('user', null);
|
||||
c.set('session', null);
|
||||
return next();
|
||||
}
|
||||
|
||||
const { session, user } = await lucia.validateSession(sessionId);
|
||||
if (session?.fresh) {
|
||||
c.header('Set-Cookie', createSessionTokenCookie(session.id).serialize(), { append: true });
|
||||
}
|
||||
if (!session) {
|
||||
c.header('Set-Cookie', lucia.createBlankSessionCookie().serialize(), { append: true });
|
||||
const { session, user } = await sessionService.validateSessionToken(sessionId);
|
||||
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,
|
||||
});
|
||||
} 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,
|
||||
});
|
||||
}
|
||||
c.set('session', session);
|
||||
c.set('user', user);
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import { Unauthorized } from '$lib/server/api/common/exceptions'
|
||||
import type { MiddlewareHandler } from 'hono'
|
||||
import { createMiddleware } from 'hono/factory'
|
||||
import type { Session, User } from 'lucia'
|
||||
import { Unauthorized } from '$lib/server/api/common/exceptions';
|
||||
import type { Sessions } from '$lib/server/api/databases/postgres/tables';
|
||||
import type { MiddlewareHandler } from 'hono';
|
||||
import { createMiddleware } from 'hono/factory';
|
||||
import type { User } from 'lucia';
|
||||
|
||||
export const requireAuth: MiddlewareHandler<{
|
||||
Variables: {
|
||||
session: Session
|
||||
user: User
|
||||
}
|
||||
session: Sessions;
|
||||
user: User;
|
||||
};
|
||||
}> = createMiddleware(async (c, next) => {
|
||||
const user = c.var.user
|
||||
if (!user) throw Unauthorized('You must be logged in to access this resource')
|
||||
return next()
|
||||
})
|
||||
const user = c.var.user;
|
||||
if (!user) throw Unauthorized('You must be logged in to access this resource');
|
||||
return next();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { drizzle } from 'drizzle-orm/node-postgres';
|
||||
import { Pool } from 'pg';
|
||||
import pg from 'pg';
|
||||
import { config } from '../common/config';
|
||||
import * as schema from '../databases/postgres/tables';
|
||||
|
||||
// create the connection
|
||||
export const pool = new Pool({
|
||||
export const pool = new pg.Pool({
|
||||
user: config.postgres.user,
|
||||
password: config.postgres.password,
|
||||
host: config.postgres.host,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { cookieExpiresAt, cookieExpiresMilliseconds, halfCookieExpiresMilliseconds } from '$lib/server/api/common/utils/cookies';
|
||||
import { SessionsRepository } from '$lib/server/api/repositories/sessions.repository';
|
||||
import { sha256 } from '@oslojs/crypto/sha2';
|
||||
import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding';
|
||||
import { TimeSpan } from 'lucia';
|
||||
import { inject, injectable } from 'tsyringe';
|
||||
import type { Sessions, Users } from '../databases/postgres/tables';
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ export class SessionsService {
|
|||
const session: Sessions = {
|
||||
id: sessionId,
|
||||
userId,
|
||||
expiresAt: new Date(Date.now() + new TimeSpan(2, 'w').seconds()),
|
||||
expiresAt: cookieExpiresAt,
|
||||
ipCountry,
|
||||
ipAddress,
|
||||
twoFactorAuthEnabled,
|
||||
|
|
@ -40,8 +40,8 @@ export class SessionsService {
|
|||
}
|
||||
|
||||
async validateSessionToken(token: string): Promise<SessionValidationResult> {
|
||||
const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
||||
const sessions = await this.sessionsRepository.findBySessionId(sessionId);
|
||||
// const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
|
||||
const sessions = await this.sessionsRepository.findBySessionId(token);
|
||||
if (sessions.length < 1) {
|
||||
return {
|
||||
session: null,
|
||||
|
|
@ -50,16 +50,16 @@ export class SessionsService {
|
|||
}
|
||||
const { user, session } = sessions[0];
|
||||
if (Date.now() >= session.expiresAt.getTime()) {
|
||||
await this.sessionsRepository.deleteBySessionId(sessionId);
|
||||
await this.sessionsRepository.deleteBySessionId(token);
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (Date.now() >= session.expiresAt.getTime() - new TimeSpan(1, 'w').seconds()) {
|
||||
session.expiresAt = new Date(Date.now() + new TimeSpan(2, 'w').seconds());
|
||||
await this.sessionsRepository.updateSessionExpiresAt(sessionId, session.expiresAt);
|
||||
if (Date.now() >= session.expiresAt.getTime() - cookieExpiresMilliseconds) {
|
||||
session.expiresAt = new Date(Date.now() + halfCookieExpiresMilliseconds);
|
||||
await this.sessionsRepository.updateSessionExpiresAt(token, session.expiresAt);
|
||||
}
|
||||
|
||||
return { session, user };
|
||||
|
|
|
|||
Loading…
Reference in a new issue