mirror of
https://github.com/BradNut/boredgame
synced 2025-09-08 17:40:22 +00:00
Fixing login cookie max-age.
This commit is contained in:
parent
8894fbf98b
commit
eeca4e4103
7 changed files with 66 additions and 41 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const signInEmailDto = z.object({
|
||||
export const signinUsernameDto = z.object({
|
||||
username: z
|
||||
.string()
|
||||
.trim()
|
||||
|
|
@ -9,4 +9,4 @@ export const signInEmailDto = z.object({
|
|||
password: z.string({ required_error: 'Password is required' }).trim(),
|
||||
});
|
||||
|
||||
export type SignInEmailDto = z.infer<typeof signInEmailDto>;
|
||||
export type SigninUsernameDto = z.infer<typeof signinUsernameDto>;
|
||||
|
|
@ -1,27 +1,43 @@
|
|||
import 'reflect-metadata';
|
||||
import { Hono } from 'hono';
|
||||
import { setCookie } from 'hono/cookie';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { inject, injectable } from 'tsyringe';
|
||||
import { TimeSpan } from 'oslo';
|
||||
import type { HonoTypes } from '../types';
|
||||
import { limiter } from '../middleware/rate-limiter.middleware';
|
||||
import type { Controller } from '../interfaces/controller.interface';
|
||||
import { signInEmailDto } from '$lib/dtos/signin-email.dto';
|
||||
import { LoginRequestsService } from '../services/loginrequest.service';
|
||||
import { signinUsernameDto } from "$lib/dtos/signin-username.dto";
|
||||
import {LuciaProvider} from "$lib/server/api/providers";
|
||||
|
||||
@injectable()
|
||||
export class LoginController implements Controller {
|
||||
controller = new Hono<HonoTypes>();
|
||||
|
||||
constructor(
|
||||
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService
|
||||
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService,
|
||||
@inject(LuciaProvider) private lucia: LuciaProvider
|
||||
) { }
|
||||
|
||||
routes() {
|
||||
return this.controller
|
||||
.post('/', zValidator('json', signInEmailDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
||||
.post('/', zValidator('json', signinUsernameDto), limiter({ limit: 10, minutes: 60 }), async (c) => {
|
||||
const { username, password } = c.req.valid('json');
|
||||
await this.loginRequestsService.verify({ username, password }, c.req);
|
||||
return c.json({ message: 'Verification email sent' });
|
||||
const session = await this.loginRequestsService.verify({ username, password }, c.req);
|
||||
const sessionCookie = this.lucia.createSessionCookie(session.id);
|
||||
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
|
||||
});
|
||||
return c.json({ message: 'ok' });
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ export const lucia = new Lucia(adapter, {
|
|||
...attributes,
|
||||
};
|
||||
},
|
||||
sessionExpiresIn: new TimeSpan(30, 'd'), // 30 days
|
||||
sessionExpiresIn: new TimeSpan(2, 'w'), // 2 weeks
|
||||
sessionCookie: {
|
||||
name: 'session',
|
||||
expires: false, // session cookies have very long lifespan (2 years)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { MailerService } from './mailer.service';
|
|||
import { TokensService } from './tokens.service';
|
||||
import { LuciaProvider } from '../providers/lucia.provider';
|
||||
import { UsersRepository } from '../repositories/users.repository';
|
||||
import type { SignInEmailDto } from '../../../dtos/signin-email.dto';
|
||||
import { CredentialsRepository } from '../repositories/credentials.repository';
|
||||
import type { HonoRequest } from 'hono';
|
||||
import type {SigninUsernameDto} from "$lib/dtos/signin-username.dto";
|
||||
|
||||
@injectable()
|
||||
export class LoginRequestsService {
|
||||
|
|
@ -32,7 +32,7 @@ export class LoginRequestsService {
|
|||
// });
|
||||
// }
|
||||
|
||||
async verify(data: SignInEmailDto, req: HonoRequest) {
|
||||
async verify(data: SigninUsernameDto, req: HonoRequest) {
|
||||
const requestIpAddress = req.header('x-real-ip');
|
||||
const requestIpCountry = req.header('x-vercel-ip-country');
|
||||
const existingUser = await this.usersRepository.findOneByUsername(data.username);
|
||||
|
|
@ -47,7 +47,7 @@ export class LoginRequestsService {
|
|||
throw BadRequest('Invalid credentials');
|
||||
}
|
||||
|
||||
if (!await this.tokensService.verifyHashedToken(credential.hashedPassword, data.password)) {
|
||||
if (!await this.tokensService.verifyHashedToken(credential.secret_data, data.password)) {
|
||||
throw BadRequest('Invalid credentials');
|
||||
}
|
||||
|
||||
|
|
@ -58,15 +58,15 @@ export class LoginRequestsService {
|
|||
ip_address: requestIpAddress || 'unknown',
|
||||
twoFactorAuthEnabled:
|
||||
!!totpCredentials &&
|
||||
totpCredentials?.secret !== null &&
|
||||
totpCredentials?.secret !== '',
|
||||
totpCredentials?.secret_data !== null &&
|
||||
totpCredentials?.secret_data !== '',
|
||||
isTwoFactorAuthenticated: false,
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new user and send a welcome email - or other onboarding process
|
||||
private async handleNewUserRegistration(email: string) {
|
||||
const newUser = await this.usersRepository.create({ email, verified: true, avatar: null })
|
||||
const newUser = await this.usersRepository.create({ email, verified: true })
|
||||
this.mailerService.sendWelcome({ to: email, props: null });
|
||||
// TODO: add whatever onboarding process or extra data you need here
|
||||
return newUser
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { fail, error, type Actions } from '@sveltejs/kit';
|
||||
import { fail, type Actions } from '@sveltejs/kit';
|
||||
import { eq, or } from 'drizzle-orm';
|
||||
import { Argon2id } from 'oslo/password';
|
||||
import { zod } from 'sveltekit-superforms/adapters';
|
||||
|
|
@ -7,46 +7,57 @@ import { redirect } from 'sveltekit-flash-message/server';
|
|||
import { RateLimiter } from 'sveltekit-rate-limiter/server';
|
||||
import db from '../../../db';
|
||||
import { lucia } from '$lib/server/auth';
|
||||
import { signInSchema } from '$lib/validations/auth';
|
||||
import { twoFactor, usersTable, type Users } from '$db/schema';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
import {signinUsernameDto} from "$lib/dtos/signin-username.dto";
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
const { locals, cookies } = event;
|
||||
const { user, session } = event.locals;
|
||||
const { locals } = event;
|
||||
|
||||
if (userFullyAuthenticated(user, session)) {
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
||||
if (authedUser) {
|
||||
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||
throw redirect('/', message, event);
|
||||
} else if (userNotFullyAuthenticated(user, session)) {
|
||||
await lucia.invalidateSession(locals.session!.id!);
|
||||
const sessionCookie = lucia.createBlankSessionCookie();
|
||||
cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
path: '.',
|
||||
...sessionCookie.attributes,
|
||||
});
|
||||
}
|
||||
const form = await superValidate(event, zod(signInSchema));
|
||||
|
||||
// if (userFullyAuthenticated(user, session)) {
|
||||
// const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||
// throw redirect('/', message, event);
|
||||
// } else if (userNotFullyAuthenticated(user, session)) {
|
||||
// await lucia.invalidateSession(locals.session!.id!);
|
||||
// const sessionCookie = lucia.createBlankSessionCookie();
|
||||
// cookies.set(sessionCookie.name, sessionCookie.value, {
|
||||
// path: '.',
|
||||
// ...sessionCookie.attributes,
|
||||
// });
|
||||
// }
|
||||
const form = await superValidate(event, zod(signinUsernameDto));
|
||||
|
||||
return {
|
||||
form,
|
||||
};
|
||||
};
|
||||
|
||||
const limiter = new RateLimiter({
|
||||
// A rate is defined by [number, unit]
|
||||
IPUA: [5, 'm'],
|
||||
});
|
||||
|
||||
export const actions: Actions = {
|
||||
default: async (event) => {
|
||||
if (await limiter.isLimited(event)) {
|
||||
throw error(429);
|
||||
}
|
||||
// if (await limiter.isLimited(event)) {
|
||||
// throw error(429);
|
||||
// }
|
||||
|
||||
const { locals } = event;
|
||||
const form = await superValidate(event, zod(signInSchema));
|
||||
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
||||
if (authedUser) {
|
||||
const message = { type: 'success', message: 'You are already signed in' } as const;
|
||||
throw redirect('/', message, event);
|
||||
}
|
||||
|
||||
const form = await superValidate(event, zod(signinUsernameDto));
|
||||
|
||||
const { error } = await locals.api.login.$post({ json: form.data }).then(locals.parseApiResponse);
|
||||
if (error) return setError(form, 'username', error);
|
||||
|
||||
if (!form.valid) {
|
||||
form.data.password = '';
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@
|
|||
<form method="POST" use:enhance>
|
||||
<Form.Field form={superLoginForm} name="username">
|
||||
<Form.Control let:attrs>
|
||||
<Form.Label for="username">Username/Email</Form.Label>
|
||||
<Form.Label for="username">Username</Form.Label>
|
||||
<Input {...attrs} autocomplete="username" bind:value={$loginForm.username} />
|
||||
</Form.Control>
|
||||
<Form.FieldErrors />
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import { add_user_to_role } from '$server/roles';
|
|||
import db from '../../../db';
|
||||
import { collections, usersTable, wishlists } from '$db/schema';
|
||||
import { createId as cuid2 } from '@paralleldrive/cuid2';
|
||||
import { userFullyAuthenticated, userNotFullyAuthenticated } from '$lib/server/auth-utils';
|
||||
|
||||
const limiter = new RateLimiter({
|
||||
// A rate is defined by [number, unit]
|
||||
|
|
@ -30,8 +29,7 @@ const signUpDefaults = {
|
|||
};
|
||||
|
||||
export const load: PageServerLoad = async (event) => {
|
||||
const { locals, cookies } = event;
|
||||
const { user, session } = event.locals;
|
||||
const { locals } = event;
|
||||
|
||||
const authedUser = await locals.getAuthedUser();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue