Skip to content

Commit

Permalink
refactor auth
Browse files Browse the repository at this point in the history
  • Loading branch information
andre-brandao committed Oct 13, 2024
1 parent 20b529f commit 1b34cc9
Show file tree
Hide file tree
Showing 17 changed files with 251 additions and 262 deletions.
2 changes: 1 addition & 1 deletion src/hooks.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
deleteSessionTokenCookie,
setSessionTokenCookie,
validateSessionToken,
} from '$lib/server/auth'
} from '$lib/server/auth/sessions'
import type { Handle } from '@sveltejs/kit'
import { sequence } from '@sveltejs/kit/hooks'

Expand Down
106 changes: 0 additions & 106 deletions src/lib/server/auth.ts

This file was deleted.

23 changes: 23 additions & 0 deletions src/lib/server/auth/cookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { RequestEvent } from '@sveltejs/kit'

export function setSessionTokenCookie(
event: RequestEvent,
token: string,
expiresAt: Date,
): void {
event.cookies.set('session', token, {
httpOnly: true,
sameSite: 'lax',
expires: expiresAt,
path: '/',
})
}

export function deleteSessionTokenCookie(event: RequestEvent): void {
event.cookies.set('session', '', {
httpOnly: true,
sameSite: 'lax',
maxAge: 0,
path: '/',
})
}
10 changes: 10 additions & 0 deletions src/lib/server/auth/oauth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Google } from 'arctic'
import { GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET } from '$env/static/private'
import { PUBLIC_DOMAIN } from '$env/static/public'
import { dev } from '$app/environment'

export const google = new Google(
GOOGLE_CLIENT_ID,
GOOGLE_CLIENT_SECRET,
`${dev ? 'http' : 'https'}://${PUBLIC_DOMAIN}/login/google/callback`,
)
88 changes: 88 additions & 0 deletions src/lib/server/auth/sessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { encodeBase32LowerCaseNoPadding } from '@oslojs/encoding'
import { encodeHexLowerCase } from '@oslojs/encoding'
import { sha256 } from '@oslojs/crypto/sha2'

import { db } from '$db'

import {
sessionTable,
userTable,
type SelectUser,
type SelectSession,
} from '$db/schema'
import { eq } from 'drizzle-orm'

export function generateId(len: number): string {
const bytes = new Uint8Array(len)
crypto.getRandomValues(bytes)
const token = encodeBase32LowerCaseNoPadding(bytes)
return token
}

export const sessionsC = {
generateSessionToken: function (): string {
const bytes = new Uint8Array(20)
crypto.getRandomValues(bytes)
const token = encodeBase32LowerCaseNoPadding(bytes)
return token
},

createSession: async function (
token: string,
userId: string,
): Promise<SelectSession> {
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token)),
)
const session: SelectSession = {
id: sessionId,
userId,
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 30),
}
await db.insert(sessionTable).values(session)
return session
},

validateSessionToken: async function (
token: string,
): Promise<SessionValidationResult> {
const sessionId = encodeHexLowerCase(
sha256(new TextEncoder().encode(token)),
)
const result = await db
.select({ user: userTable, session: sessionTable })
.from(sessionTable)
.innerJoin(userTable, eq(sessionTable.userId, userTable.id))
.where(eq(sessionTable.id, sessionId))
if (result.length < 1) {
return { session: null, user: null }
}
const { user, session } = result[0]
if (Date.now() >= session.expiresAt.getTime()) {
await db.delete(sessionTable).where(eq(sessionTable.id, session.id))
return { session: null, user: null }
}
if (Date.now() >= session.expiresAt.getTime() - 1000 * 60 * 60 * 24 * 15) {
session.expiresAt = new Date(Date.now() + 1000 * 60 * 60 * 24 * 30)
await db
.update(sessionTable)
.set({
expiresAt: session.expiresAt,
})
.where(eq(sessionTable.id, session.id))
}
return { session, user }
},

invalidateSession: async function (sessionId: string): Promise<void> {
await db.delete(sessionTable).where(eq(sessionTable.id, sessionId))
},

invalidateUserSessions: async function (userId: string): Promise<void> {
await db.delete(sessionTable).where(eq(sessionTable.userId, userId))
},
}

export type SessionValidationResult =
| { session: SelectSession; user: SelectUser }
| { session: null; user: null }
84 changes: 84 additions & 0 deletions src/lib/server/db/tenant/customer/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-disable @typescript-eslint/no-unused-vars */
import {
sqliteTable,
text,
integer,
// customType,
} from 'drizzle-orm/sqlite-core'
import { sql, relations } from 'drizzle-orm'

import { userTable, productItemTable } from '$db/schema'
import { createInsertSchema } from 'drizzle-zod'

export const addressTable = sqliteTable('address', {
id: integer('id').notNull().primaryKey({ autoIncrement: true }),
created_at: integer('created_at', { mode: 'timestamp' }).default(
sql`(CURRENT_TIMESTAMP)`,
),

user_id: text('user_id')
.notNull()
.references(() => userTable.id),
is_default: integer('is_default', { mode: 'boolean' }).default(false),
cep: text('cep').notNull(),
street: text('street').notNull(),
number: text('number').notNull(),
complement: text('complement').notNull(),
neighborhood: text('neighborhood').notNull(),
city: text('city').notNull(),
state: text('state').notNull(),
country: text('country').notNull(),
})

export const insertAddressSchema = createInsertSchema(addressTable)
export type SelectAddress = typeof addressTable.$inferSelect
export type InsertAddress = typeof addressTable.$inferInsert

export const customerOrderTable = sqliteTable('customer_order', {
id: integer('id').notNull().primaryKey({ autoIncrement: true }),
// .$defaultFn(() => generateId(15)),
created_at: integer('created_at', { mode: 'timestamp' }).default(
sql`(CURRENT_TIMESTAMP)`,
),

user_id: text('customer_id')
.notNull()
.references(() => userTable.id),
address_id: integer('address_id').references(() => addressTable.id),
payment_method: text('payment_method').notNull(),
total: integer('total').notNull(),
observation: text('observation'),
status: text('status', {
enum: [
'PENDING',
'CONFIRMED',
'PREPARING',
'ON THE WAY',
'DELIVERED',
'CANCELED',
],
}).notNull(),
})

export type SelectCustomerOrder = typeof customerOrderTable.$inferSelect
export type InsertCustomerOrder = typeof customerOrderTable.$inferInsert

export const orderItemTable = sqliteTable('order_item', {
id: integer('id').notNull().primaryKey({ autoIncrement: true }),
created_at: integer('created_at', { mode: 'timestamp' }).default(
sql`(CURRENT_TIMESTAMP)`,
),

order_id: integer('order_id')
.notNull()
.references(() => customerOrderTable.id),
product_id: integer('product_id')
.notNull()
.references(() => productItemTable.id),
observation: text('observation'),
quantity: integer('quantity').notNull(),
price: integer('price').notNull(),
})

export type SelectOrderItem = typeof orderItemTable.$inferSelect
export type InsertOrderItem = typeof orderItemTable.$inferInsert
2 changes: 1 addition & 1 deletion src/lib/server/db/user/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { encodeHex } from 'oslo/encoding'
import { hash, verify } from './password'
import { LibsqlError } from '@libsql/client'
import { emailTemplate, sendMail } from '$lib/server/services/email'
import { generateId } from '$lib/server/auth'
import { generateId } from '$lib/server/auth/sessions'

export function isValidEmail(email: string): boolean {
return /.+@.+/.test(email)
Expand Down
4 changes: 2 additions & 2 deletions src/lib/server/db/user/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
invalidateUserSessions,
setSessionTokenCookie,
validateSessionToken,
} from '$lib/server/auth'
} from '$lib/server/auth/sessions'

export const userRouter = router({
resendEmailVerification: publicProcedure.query(async ({ ctx }) => {
Expand Down Expand Up @@ -48,7 +48,7 @@ export const userRouter = router({
}),
)
.query(async ({ ctx, input }) => {
const { locals } = ctx
const { locals } = ctx
const sessionId = locals.session?.id
const { code } = input

Expand Down
2 changes: 1 addition & 1 deletion src/lib/server/db/user/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
// customType,
} from 'drizzle-orm/sqlite-core'
import { relations, sql } from 'drizzle-orm'
import { generateId, generateSessionToken } from '$lib/server/auth'
import { generateId, generateSessionToken } from '$lib/server/auth/sessions'

export const userRoleEnum = ['customer', 'admin'] as const
export type UserRole = (typeof userRoleEnum)[number]
Expand Down
9 changes: 4 additions & 5 deletions src/routes/(auth)/login/+page.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,9 @@ import type { Actions, PageServerLoad } from './$types'

import { user } from '$db/controller'
import {
deleteSessionTokenCookie,
invalidateSession,

} from '$lib/server/auth'
sessionsC,
} from '$lib/server/auth/sessions'
import { deleteSessionTokenCookie } from '$lib/server/auth/cookies'
// import { emailTemplate, sendMail } from '$lib/server/email'

export const load: PageServerLoad = async event => {
Expand Down Expand Up @@ -49,7 +48,7 @@ export const actions: Actions = {
if (!session) {
return redirect(302, '/login')
}
await invalidateSession(session.id)
await sessionsC.invalidateSession(session.id)
deleteSessionTokenCookie(event)
return redirect(302, '/login')
},
Expand Down
Loading

0 comments on commit 1b34cc9

Please sign in to comment.