diff --git a/README.md b/README.md index 2bd52016..b480db2e 100644 --- a/README.md +++ b/README.md @@ -188,6 +188,25 @@ export default oauth.githubEventHandler({ Make sure to set the callback URL in your OAuth app settings as `/auth/github`. +### Extend Session + +We leverage hooks to let you extend the session data with your own data or to log when the user clear its session. + +```ts +// server/plugins/session.ts +export default defineNitroPlugin(() => { + sessionHooks.hook('verify', async (session, event) => { + // extend User Session by calling your database + // or + // throw createError({ ... }) if session is invalid for example + }) + + sessionHooks.hook('clear', async (session, event) => { + // Log that user logged out + }) +}) +``` + ## Development ```bash diff --git a/package.json b/package.json index f1dec878..6ab2c6cb 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "dependencies": { "@nuxt/kit": "^3.8.2", "defu": "^6.1.3", + "hookable": "^5.5.3", "ofetch": "^1.3.3", "ohash": "^1.1.3" }, diff --git a/playground/auth.d.ts b/playground/auth.d.ts index 7e648152..eacee58a 100644 --- a/playground/auth.d.ts +++ b/playground/auth.d.ts @@ -11,6 +11,7 @@ declare module '#auth-utils' { battledotnet?: any linkedin?: any } + extended?: any loggedInAt: number } } diff --git a/playground/server/plugins/session.ts b/playground/server/plugins/session.ts new file mode 100644 index 00000000..1da62b77 --- /dev/null +++ b/playground/server/plugins/session.ts @@ -0,0 +1,14 @@ +export default defineNitroPlugin(() => { + sessionHooks.hook('verify', async (session) => { + // Extend User Session + // Or throw createError({ ... }) if session is invalid + session.extended = { + fromHooks: true + } + }) + + sessionHooks.hook('clear', async () => { + // Log that user logged out + }) +}) + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cfde10d3..dc036e7f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: defu: specifier: ^6.1.3 version: 6.1.3 + hookable: + specifier: ^5.5.3 + version: 5.5.3 ofetch: specifier: ^1.3.3 version: 1.3.3 diff --git a/src/module.ts b/src/module.ts index af2cc4f4..d34867b6 100644 --- a/src/module.ts +++ b/src/module.ts @@ -39,6 +39,7 @@ export default defineNuxtModule({ { from: resolver.resolve('./runtime/server/utils/session'), imports: [ + 'sessionHooks', 'getUserSession', 'setUserSession', 'clearUserSession', diff --git a/src/runtime/server/api/session.get.ts b/src/runtime/server/api/session.get.ts index 1f76d59d..664dff45 100644 --- a/src/runtime/server/api/session.get.ts +++ b/src/runtime/server/api/session.get.ts @@ -1,6 +1,10 @@ import { eventHandler } from 'h3' -import { requireUserSession } from '../utils/session' +import { requireUserSession, sessionHooks } from '../utils/session' export default eventHandler(async (event) => { - return await requireUserSession(event) + const session = await requireUserSession(event) + + await sessionHooks.callHookParallel('verify', event, session) + + return session }) diff --git a/src/runtime/server/utils/session.ts b/src/runtime/server/utils/session.ts index e2f38b5d..36ae454b 100644 --- a/src/runtime/server/utils/session.ts +++ b/src/runtime/server/utils/session.ts @@ -1,9 +1,25 @@ import type { H3Event, SessionConfig } from 'h3' import { useSession, createError } from 'h3' import { defu } from 'defu' +import { createHooks } from 'hookable' import { useRuntimeConfig } from '#imports' import type { UserSession } from '#auth-utils' +export interface SessionHooks { + /** + * Called when fetching the session from the API + * - Add extra properties to the session + * - Throw an error if the session could not be verified (with a database for example) + */ + 'verify': (session: UserSession, event: H3Event) => void | Promise + /** + * Called before clearing the session + */ + 'clear': (session: UserSession, event: H3Event) => void | Promise +} + +export const sessionHooks = createHooks() + export async function getUserSession (event: H3Event) { return (await _useSession(event)).data } @@ -23,6 +39,7 @@ export async function setUserSession (event: H3Event, data: UserSession) { export async function clearUserSession (event: H3Event) { const session = await _useSession(event) + await sessionHooks.callHookParallel('clear', event, session) await session.clear() return true