@@ -212,11 +212,14 @@ export default defineAdder({
212212 imports . addNamespace ( ast , '$lib/server/db/schema' , 'table' ) ;
213213 imports . addNamed ( ast , '$lib/server/db' , { db : 'db' } ) ;
214214 imports . addNamed ( ast , '@oslojs/encoding' , {
215- encodeBase32LowerCaseNoPadding : 'encodeBase32LowerCaseNoPadding ' ,
215+ encodeBase64url : 'encodeBase64url ' ,
216216 encodeHexLowerCase : 'encodeHexLowerCase'
217217 } ) ;
218218 imports . addNamed ( ast , '@oslojs/crypto/sha2' , { sha256 : 'sha256' } ) ;
219219 imports . addNamed ( ast , 'drizzle-orm' , { eq : 'eq' } ) ;
220+ if ( typescript ) {
221+ imports . addNamed ( ast , '@sveltejs/kit' , { RequestEvent : 'RequestEvent' } , true ) ;
222+ }
220223
221224 const ms = new MagicString ( generateCode ( ) . trim ( ) ) ;
222225 const [ ts ] = utils . createPrinter ( typescript ) ;
@@ -227,21 +230,22 @@ export default defineAdder({
227230 if ( ! ms . original . includes ( 'export const sessionCookieName' ) ) {
228231 ms . append ( "\n\nexport const sessionCookieName = 'auth-session';" ) ;
229232 }
230- if ( ! ms . original . includes ( 'function generateSessionToken' ) ) {
233+ if ( ! ms . original . includes ( 'export function generateSessionToken' ) ) {
231234 const generateSessionToken = dedent `
232- ${ ts ( '' , '/** @returns {string} */' ) }
233- function generateSessionToken()${ ts ( ': string' ) } {
234- const bytes = crypto.getRandomValues(new Uint8Array(20));
235- const token = encodeBase32LowerCaseNoPadding(bytes);
235+ export function generateSessionToken() {
236+ const bytes = crypto.getRandomValues(new Uint8Array(18));
237+ const token = encodeBase64url(bytes);
236238 return token;
237239 }` ;
238240 ms . append ( `\n\n${ generateSessionToken } ` ) ;
239241 }
240242 if ( ! ms . original . includes ( 'async function createSession' ) ) {
241- const createSession = dedent `
242- ${ ts ( '' , '/** @param {string} userId */' ) }
243- export async function createSession(userId${ ts ( ': string' ) } )${ ts ( ': Promise<table.Session>' ) } {
244- const token = generateSessionToken();
243+ const createSession = dedent `
244+ ${ ts ( '' , '/**' ) }
245+ ${ ts ( '' , ' * @param {string} token' ) }
246+ ${ ts ( '' , ' * @param {string} userId' ) }
247+ ${ ts ( '' , ' */' ) }
248+ export async function createSession(token${ ts ( ': string' ) } , userId${ ts ( ': string' ) } ) {
245249 const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
246250 const session${ ts ( ': table.Session' ) } = {
247251 id: sessionId,
@@ -253,21 +257,11 @@ export default defineAdder({
253257 }` ;
254258 ms . append ( `\n\n${ createSession } ` ) ;
255259 }
256- if ( ! ms . original . includes ( 'async function invalidateSession' ) ) {
257- const invalidateSession = dedent `
258- ${ ts ( '' , '/**' ) }
259- ${ ts ( '' , ' * @param {string} sessionId' ) }
260- ${ ts ( '' , ' * @returns {Promise<void>}' ) }
261- ${ ts ( '' , ' */' ) }
262- export async function invalidateSession(sessionId${ ts ( ': string' ) } )${ ts ( ': Promise<void>' ) } {
263- await db.delete(table.session).where(eq(table.session.id, sessionId));
264- }` ;
265- ms . append ( `\n\n${ invalidateSession } ` ) ;
266- }
267- if ( ! ms . original . includes ( 'async function validateSession' ) ) {
268- const validateSession = dedent `
269- ${ ts ( '' , '/** @param {string} sessionId */' ) }
270- export async function validateSession(sessionId${ ts ( ': string' ) } ) {
260+ if ( ! ms . original . includes ( 'async function validateSessionToken' ) ) {
261+ const validateSessionToken = dedent `
262+ ${ ts ( '' , '/** @param {string} token */' ) }
263+ export async function validateSessionToken(token${ ts ( ': string' ) } ) {
264+ const sessionId = encodeHexLowerCase(sha256(new TextEncoder().encode(token)));
271265 const [result] = await db
272266 .select({
273267 // Adjust user table here to tweak returned data
@@ -300,14 +294,46 @@ export default defineAdder({
300294
301295 return { session, user };
302296 }` ;
303- ms . append ( `\n\n${ validateSession } ` ) ;
297+ ms . append ( `\n\n${ validateSessionToken } ` ) ;
304298 }
305299 if ( typescript && ! ms . original . includes ( 'export type SessionValidationResult' ) ) {
306300 const sessionType =
307- 'export type SessionValidationResult = Awaited<ReturnType<typeof validateSession >>;' ;
301+ 'export type SessionValidationResult = Awaited<ReturnType<typeof validateSessionToken >>;' ;
308302 ms . append ( `\n\n${ sessionType } ` ) ;
309303 }
310-
304+ if ( ! ms . original . includes ( 'async function invalidateSession' ) ) {
305+ const invalidateSession = dedent `
306+ ${ ts ( '' , '/** @param {string} sessionId */' ) }
307+ export async function invalidateSession(sessionId${ ts ( ': string' ) } ) {
308+ await db.delete(table.session).where(eq(table.session.id, sessionId));
309+ }` ;
310+ ms . append ( `\n\n${ invalidateSession } ` ) ;
311+ }
312+ if ( ! ms . original . includes ( 'export function setSessionTokenCookie' ) ) {
313+ const setSessionTokenCookie = dedent `
314+ ${ ts ( '' , '/**' ) }
315+ ${ ts ( '' , ' * @param {import("@sveltejs/kit").RequestEvent} event' ) }
316+ ${ ts ( '' , ' * @param {string} token' ) }
317+ ${ ts ( '' , ' * @param {Date} expiresAt' ) }
318+ ${ ts ( '' , ' */' ) }
319+ export function setSessionTokenCookie(event${ ts ( ': RequestEvent' ) } , token${ ts ( ': string' ) } , expiresAt${ ts ( ': Date' ) } ) {
320+ event.cookies.set(sessionCookieName, token, {
321+ expires: expiresAt,
322+ path: '/'
323+ });
324+ }` ;
325+ ms . append ( `\n\n${ setSessionTokenCookie } ` ) ;
326+ }
327+ if ( ! ms . original . includes ( 'export function deleteSessionTokenCookie' ) ) {
328+ const deleteSessionTokenCookie = dedent `
329+ ${ ts ( '' , '/** @param {import("@sveltejs/kit").RequestEvent} event */' ) }
330+ export function deleteSessionTokenCookie(event${ ts ( ': RequestEvent' ) } ) {
331+ event.cookies.delete(sessionCookieName, {
332+ path: '/'
333+ });
334+ }` ;
335+ ms . append ( `\n\n${ deleteSessionTokenCookie } ` ) ;
336+ }
311337 return ms . toString ( ) ;
312338 }
313339 } ,
@@ -339,7 +365,6 @@ export default defineAdder({
339365 content : ( { content, typescript } ) => {
340366 const { ast, generateCode } = parseScript ( content ) ;
341367 imports . addNamespace ( ast , '$lib/server/auth.js' , 'auth' ) ;
342- imports . addNamed ( ast , '$app/environment' , { dev : 'dev' } ) ;
343368 kit . addHooksHandle ( ast , typescript , 'handleAuth' , getAuthHandleContent ( ) ) ;
344369 return generateCode ( ) ;
345370 }
@@ -365,10 +390,9 @@ export default defineAdder({
365390 const [ ts ] = utils . createPrinter ( typescript ) ;
366391 return dedent `
367392 import { hash, verify } from '@node-rs/argon2';
368- import { generateRandomString } from '@oslojs/crypto/random ';
393+ import { encodeBase64url } from '@oslojs/encoding ';
369394 import { fail, redirect } from '@sveltejs/kit';
370395 import { eq } from 'drizzle-orm';
371- import { dev } from '$app/environment';
372396 import * as auth from '$lib/server/auth';
373397 import { db } from '$lib/server/db';
374398 import * as table from '$lib/server/db/schema';
@@ -413,14 +437,9 @@ export default defineAdder({
413437 return fail(400, { message: 'Incorrect username or password' });
414438 }
415439
416- const session = await auth.createSession(existingUser.id);
417- event.cookies.set(auth.sessionCookieName, session.id, {
418- path: '/',
419- sameSite: 'lax',
420- httpOnly: true,
421- expires: session.expiresAt,
422- secure: !dev
423- });
440+ const sessionToken = auth.generateSessionToken();
441+ const session = await auth.createSession(sessionToken, existingUser.id);
442+ auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
424443
425444 return redirect(302, '/demo/lucia');
426445 },
@@ -448,25 +467,21 @@ export default defineAdder({
448467 try {
449468 await db.insert(table.user).values({ id: userId, username, passwordHash });
450469
451- const session = await auth.createSession(userId);
452- event.cookies.set(auth.sessionCookieName, session.id, {
453- path: '/',
454- sameSite: 'lax',
455- httpOnly: true,
456- expires: session.expiresAt,
457- secure: !dev
458- });
470+ const sessionToken = auth.generateSessionToken();
471+ const session = await auth.createSession(sessionToken, userId);
472+ auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
459473 } catch (e) {
460474 return fail(500, { message: 'An error has occurred' });
461475 }
462476 return redirect(302, '/demo/lucia');
463477 },
464478 };
465479
466- const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_';
467-
468- function generateUserId(length = 21)${ ts ( ': string' ) } {
469- return generateRandomString({ read: (bytes) => crypto.getRandomValues(bytes) }, alphabet, length);
480+ function generateUserId() {
481+ // ID with 120 bits of entropy, or about the same as UUID v4.
482+ const bytes = crypto.getRandomValues(new Uint8Array(15));
483+ const id = encodeBase64url(bytes);
484+ return id;
470485 }
471486
472487 function validateUsername(username${ ts ( ': unknown' ) } )${ ts ( ': username is string' ) } {
@@ -554,7 +569,7 @@ export default defineAdder({
554569 return fail(401);
555570 }
556571 await auth.invalidateSession(event.locals.session.id);
557- event.cookies.delete( auth.sessionCookieName, { path: '/' } );
572+ auth.deleteSessionTokenCookie(event );
558573
559574 return redirect(302, '/demo/lucia/login');
560575 },
@@ -636,24 +651,18 @@ function createLuciaType(name: string): AstTypes.TSInterfaceBody['body'][number]
636651function getAuthHandleContent ( ) {
637652 return `
638653 async ({ event, resolve }) => {
639- const sessionId = event.cookies.get(auth.sessionCookieName);
640- if (!sessionId ) {
654+ const sessionToken = event.cookies.get(auth.sessionCookieName);
655+ if (!sessionToken ) {
641656 event.locals.user = null;
642657 event.locals.session = null;
643658 return resolve(event);
644659 }
645660
646- const { session, user } = await auth.validateSession(sessionId );
661+ const { session, user } = await auth.validateSessionToken(sessionToken );
647662 if (session) {
648- event.cookies.set(auth.sessionCookieName, session.id, {
649- path: '/',
650- sameSite: 'lax',
651- httpOnly: true,
652- expires: session.expiresAt,
653- secure: !dev
654- });
663+ auth.setSessionTokenCookie(event, sessionToken, session.expiresAt);
655664 } else {
656- event.cookies.delete( auth.sessionCookieName, { path: '/' } );
665+ auth.deleteSessionTokenCookie(event );
657666 }
658667
659668 event.locals.user = user;
0 commit comments