2024-12-25 17:08:43 +08:00
|
|
|
import { createClerkClient } from '@clerk/backend'
|
|
|
|
import type { ClerkClient, ClerkOptions } from '@clerk/backend'
|
2023-10-27 15:50:22 +08:00
|
|
|
import type { Context, MiddlewareHandler } from 'hono'
|
|
|
|
import { env } from 'hono/adapter'
|
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
type ClerkAuth = ReturnType<Awaited<ReturnType<ClerkClient['authenticateRequest']>>['toAuth']>
|
2023-10-27 15:50:22 +08:00
|
|
|
|
|
|
|
declare module 'hono' {
|
|
|
|
interface ContextVariableMap {
|
2024-04-25 21:43:36 +08:00
|
|
|
clerk: ClerkClient
|
|
|
|
clerkAuth: ClerkAuth
|
2023-10-27 15:50:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-06-09 17:11:11 +08:00
|
|
|
export const getAuth = (c: Context): ClerkAuth => {
|
2023-10-27 15:50:22 +08:00
|
|
|
return c.get('clerkAuth')
|
|
|
|
}
|
|
|
|
|
|
|
|
type ClerkEnv = {
|
|
|
|
CLERK_SECRET_KEY: string
|
|
|
|
CLERK_PUBLISHABLE_KEY: string
|
|
|
|
CLERK_API_URL: string
|
|
|
|
CLERK_API_VERSION: string
|
|
|
|
}
|
|
|
|
|
|
|
|
export const clerkMiddleware = (options?: ClerkOptions): MiddlewareHandler => {
|
|
|
|
return async (c, next) => {
|
|
|
|
const clerkEnv = env<ClerkEnv>(c)
|
|
|
|
const { secretKey, publishableKey, apiUrl, apiVersion, ...rest } = options || {
|
|
|
|
secretKey: clerkEnv.CLERK_SECRET_KEY || '',
|
|
|
|
publishableKey: clerkEnv.CLERK_PUBLISHABLE_KEY || '',
|
2024-04-25 21:43:36 +08:00
|
|
|
apiUrl: clerkEnv.CLERK_API_URL,
|
|
|
|
apiVersion: clerkEnv.CLERK_API_VERSION,
|
2023-10-27 15:50:22 +08:00
|
|
|
}
|
|
|
|
if (!secretKey) {
|
|
|
|
throw new Error('Missing Clerk Secret key')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!publishableKey) {
|
|
|
|
throw new Error('Missing Clerk Publishable key')
|
|
|
|
}
|
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
const clerkClient = createClerkClient({
|
2023-10-27 15:50:22 +08:00
|
|
|
...rest,
|
|
|
|
apiUrl,
|
|
|
|
apiVersion,
|
|
|
|
secretKey,
|
|
|
|
publishableKey,
|
|
|
|
})
|
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
const requestState = await clerkClient.authenticateRequest(c.req.raw, {
|
2023-10-27 15:50:22 +08:00
|
|
|
...rest,
|
|
|
|
secretKey,
|
|
|
|
publishableKey,
|
|
|
|
})
|
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
if (requestState.headers) {
|
|
|
|
requestState.headers.forEach((value, key) => c.res.headers.append(key, value))
|
2023-10-27 15:50:22 +08:00
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
const locationHeader = requestState.headers.get('location')
|
2024-05-04 10:33:29 +08:00
|
|
|
|
2024-04-25 21:43:36 +08:00
|
|
|
if (locationHeader) {
|
|
|
|
return c.redirect(locationHeader, 307)
|
|
|
|
} else if (requestState.status === 'handshake') {
|
|
|
|
throw new Error('Clerk: unexpected handshake without redirect')
|
|
|
|
}
|
2023-10-27 15:50:22 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
c.set('clerkAuth', requestState.toAuth())
|
|
|
|
c.set('clerk', clerkClient)
|
|
|
|
|
|
|
|
await next()
|
|
|
|
}
|
|
|
|
}
|