Skip to content

Commit

Permalink
Merge pull request #125 from rmarscher/lucia-v3-dec-23
Browse files Browse the repository at this point in the history
Update to latest lucia v3 beta, oslo and arctic
  • Loading branch information
timothymiller authored Dec 12, 2023
2 parents ae4fd94 + db8fb9a commit 59a5ccd
Show file tree
Hide file tree
Showing 21 changed files with 627 additions and 752 deletions.
30 changes: 30 additions & 0 deletions apps/next/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

// This function can be marked `async` if using `await` inside
export async function middleware(request: NextRequest) {
if (request.method === 'POST' && request.body) {
try {
const cloned = request.clone()
const requestHeaders = new Headers(request.headers)
const formData = await cloned.formData()
const userJson = formData.get('user')
if (typeof userJson === 'string') {
requestHeaders.set('x-apple-user', userJson)
}
return NextResponse.next({
request: {
// New request headers
headers: requestHeaders,
},
})
} catch (e: unknown) {
console.error('error parsing oauth post', e)
}
}
}

// See "Matching Paths" below to learn more
export const config = {
matcher: ['/oauth/apple'],
}
8 changes: 5 additions & 3 deletions apps/next/pages/oauth/[provider].tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { OAuthSignInScreen } from 'app/features/oauth/screen'
import { OAuthSignInScreen, OAuthSignInScreenProps } from 'app/features/oauth/screen'
import Head from 'next/head'

export default function Page() {
export { getServerSideProps } from 'app/features/oauth/screen'

export default function Page(props: OAuthSignInScreenProps) {
return (
<>
<Head>
<title>OAuth Sign In</title>
</Head>
<OAuthSignInScreen />
<OAuthSignInScreen {...props} />
</>
)
}
101 changes: 28 additions & 73 deletions packages/api/migrations/meta/0004_snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,34 @@
"notNull": false,
"autoincrement": false,
"default": "CURRENT_TIMESTAMP"
},
"totp_secret": {
"name": "totp_secret",
"type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"totp_expires": {
"name": "totp_expires",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"timeout_until": {
"name": "timeout_until",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"timeout_seconds": {
"name": "timeout_seconds",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
}
},
"indexes": {
Expand Down Expand Up @@ -205,79 +233,6 @@
"foreignKeys": {},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
},
"VerificationCode": {
"name": "VerificationCode",
"columns": {
"id": {
"name": "id",
"type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"user_id": {
"name": "user_id",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"code": {
"name": "code",
"type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"expires": {
"name": "expires",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"timeout_until": {
"name": "timeout_until",
"type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"timeout_seconds": {
"name": "timeout_seconds",
"type": "integer",
"primaryKey": false,
"notNull": true,
"autoincrement": false,
"default": 0
}
},
"indexes": {
"VerificationCode_user_id_unique": {
"name": "VerificationCode_user_id_unique",
"columns": ["user_id"],
"isUnique": true
},
"idx_verificationCode_userId": {
"name": "idx_verificationCode_userId",
"columns": ["user_id"],
"isUnique": false
}
},
"foreignKeys": {
"VerificationCode_user_id_User_id_fk": {
"name": "VerificationCode_user_id_User_id_fk",
"tableFrom": "VerificationCode",
"tableTo": "User",
"columnsFrom": ["user_id"],
"columnsTo": ["id"],
"onDelete": "no action",
"onUpdate": "no action"
}
},
"compositePrimaryKeys": {},
"uniqueConstraints": {}
}
},
"enums": {},
Expand Down
8 changes: 4 additions & 4 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@
"@cloudflare/workers-wasi": "^0.0.5",
"@hono/trpc-server": "^0.1.0",
"@libsql/client": "^0.3.5",
"@lucia-auth/adapter-sqlite": "3.0.0-beta.1",
"@lucia-auth/adapter-drizzle": "1.0.0-beta.2",
"@trpc/server": "^10.43.2",
"arctic": "0.3.1",
"arctic": "0.10.0",
"drizzle-orm": "^0.29.0",
"drizzle-valibot": "beta",
"hono": "^3.9.2",
"lucia": "3.0.0-beta.6",
"lucia": "3.0.0-beta.12",
"miniflare": "3.20231025.1",
"oslo": "0.22.0",
"oslo": "0.24.0",
"superjson": "1.13.3",
"ts-pattern": "^5.0.5",
"valibot": "^0.20.1"
Expand Down
42 changes: 19 additions & 23 deletions packages/api/src/auth/hono.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,22 @@
import { getAuthOptions } from './shared'
import { D1Adapter } from '@lucia-auth/adapter-sqlite'
import type { Context as HonoContext, HonoRequest } from 'hono'
import { Lucia } from 'lucia'
import type { Middleware } from 'lucia'
import { getAllowedOriginHost } from '.'
import type { Context as HonoContext, Next } from 'hono'
import { Bindings } from '../worker'
import { verifyRequestOrigin } from 'oslo/request'

export const hono = (): Middleware<[HonoContext]> => {
return ({ args }) => {
const [context] = args
return {
request: context.req,
setCookie: (cookie) => {
context.res.headers.append('set-cookie', cookie.serialize())
},
}
export const csrfMiddleware = async (c: HonoContext<{ Bindings: Bindings }>, next: Next) => {
// CSRF middleware
if (c.req.method === 'GET') {
return next()
}
const originHeader = c.req.header('origin')
const hostHeader = c.req.header('host')
const allowedOrigin = getAllowedOriginHost(c.env.APP_URL, c.req.raw)
if (
!originHeader ||
!hostHeader ||
!verifyRequestOrigin(originHeader, [hostHeader, ...(allowedOrigin ? [allowedOrigin] : [])])
) {
return c.body(null, 403)
}
return next()
}

export const createHonoAuth = (db: D1Database, appUrl: string, request?: HonoRequest) => {
return new Lucia(new D1Adapter(db, { session: 'session', user: 'user' }), {
...getAuthOptions(db, appUrl, request),
middleware: hono(),
})
}

export type HonoLucia = ReturnType<typeof createHonoAuth>
68 changes: 68 additions & 0 deletions packages/api/src/auth/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { Adapter, DatabaseSessionAttributes, DatabaseUserAttributes, Lucia, TimeSpan } from 'lucia'
import { DrizzleSQLiteAdapter } from '@lucia-auth/adapter-drizzle'
import { SessionTable, UserTable } from '../db/schema'
import { DB } from '../db/client'

/**
* Lucia's isValidRequestOrigin method will compare the
* origin of the request to the configured host.
* We want to allow cross-domain requests from our APP_URL so return that
* if the request origin host matches the APP_URL host.
* @link https://github.com/lucia-auth/lucia/blob/main/packages/lucia/src/utils/url.ts
*/
export const getAllowedOriginHost = (app_url: string, request?: Request) => {
if (!app_url || !request) return undefined
const requestOrigin = request.headers.get('Origin')
const requestHost = requestOrigin ? new URL(requestOrigin).host : undefined
const appHost = new URL(app_url).host
return requestHost === appHost ? appHost : undefined
}

export const createAuth = (db: DB, appUrl: string) => {
// @ts-ignore Expect type errors because this is D1 and not SQLite... but it works
const adapter = new DrizzleSQLiteAdapter(db, SessionTable, UserTable)
// cast probably only needed until adapter-drizzle is updated
return new Lucia(adapter as Adapter, {
...getAuthOptions(appUrl),
})
}

export const getAuthOptions = (appUrl: string) => {
const env = !appUrl || appUrl.startsWith('http:') ? 'DEV' : 'PROD'
return {
getUserAttributes: (data: DatabaseUserAttributes) => {
return {
email: data.email || '',
}
},
// Optional additional session attributes to expose
// If updated, also update createSession() in packages/api/src/auth/user.ts
getSessionAttributes: (databaseSession: DatabaseSessionAttributes) => {
return {}
},
sessionExpiresIn: new TimeSpan(365, 'd'),
sessionCookie: {
name: 'auth_session',
expires: false,
attributes: {
secure: env === 'PROD',
sameSite: 'lax' as const,
},
},

// If you want more debugging, uncomment this
// experimental: {
// debugMode: true,
// },
}
}

declare module 'lucia' {
interface Register {
Lucia: ReturnType<typeof createAuth>
}
interface DatabaseSessionAttributes {}
interface DatabaseUserAttributes {
email: string | null
}
}
22 changes: 0 additions & 22 deletions packages/api/src/auth/nextjs.ts

This file was deleted.

Loading

0 comments on commit 59a5ccd

Please sign in to comment.