diff --git a/playground-local/app.vue b/playground-local/app.vue
index 1f770a6f..982c2c9a 100644
--- a/playground-local/app.vue
+++ b/playground-local/app.vue
@@ -41,9 +41,23 @@ const password = ref('hunter2')
refresh session (required: true)
-
- navigate to Login Page
-
+
Navigation
+ Navigate to different pages below to test out different things:
+
+
+ -> API endpoint protected inline
+
+
+
+ -> API endpoint protected middleware
+
+
+
+ -> navigate to Login Page
+
+
+
+
diff --git a/playground-local/server/api/protected/inline.ts b/playground-local/server/api/protected/inline.ts
new file mode 100644
index 00000000..8a7e77d9
--- /dev/null
+++ b/playground-local/server/api/protected/inline.ts
@@ -0,0 +1,10 @@
+import { eventHandler } from 'h3'
+import { getServerSession } from '#auth'
+
+export default eventHandler(async (event) => {
+ const session = await getServerSession(event)
+ if (!session) {
+ return { status: 'unauthenticated!' }
+ }
+ return { status: 'authenticated!', text: 'im protected by an in-endpoint check', session }
+})
diff --git a/playground-local/server/api/protected/middleware.ts b/playground-local/server/api/protected/middleware.ts
new file mode 100644
index 00000000..485da1ac
--- /dev/null
+++ b/playground-local/server/api/protected/middleware.ts
@@ -0,0 +1,3 @@
+import { eventHandler } from 'h3'
+
+export default eventHandler(() => ({ status: 'authenticated', text: 'you only see me if you are logged in, as a server-middleware protects me' }))
diff --git a/playground-local/server/api/session.get.ts b/playground-local/server/api/session.get.ts
new file mode 100644
index 00000000..5d5e4b5b
--- /dev/null
+++ b/playground-local/server/api/session.get.ts
@@ -0,0 +1,4 @@
+import { defineEventHandler } from 'h3'
+import { getServerSession } from '#auth'
+
+export default defineEventHandler(event => getServerSession(event))
diff --git a/playground-local/server/api/token.get.ts b/playground-local/server/api/token.get.ts
new file mode 100644
index 00000000..cd349d63
--- /dev/null
+++ b/playground-local/server/api/token.get.ts
@@ -0,0 +1,4 @@
+import { defineEventHandler } from 'h3'
+import { getToken } from '#auth'
+
+export default defineEventHandler(event => getToken(event))
diff --git a/playground-local/server/middleware/auth.ts b/playground-local/server/middleware/auth.ts
new file mode 100644
index 00000000..8b6ce9a9
--- /dev/null
+++ b/playground-local/server/middleware/auth.ts
@@ -0,0 +1,14 @@
+import { createError, eventHandler } from 'h3'
+import { getServerSession } from '#auth'
+
+export default eventHandler(async (event) => {
+ // Only protect a certain backend route
+ if (!event.node.req.url?.startsWith('/api/protected/middleware')) {
+ return
+ }
+
+ const session = await getServerSession(event)
+ if (!session) {
+ throw createError({ statusMessage: 'Unauthenticated', statusCode: 403 })
+ }
+})
diff --git a/src/module.ts b/src/module.ts
index 1ec9f43a..bbbe2f53 100644
--- a/src/module.ts
+++ b/src/module.ts
@@ -12,7 +12,6 @@ import {
} from '@nuxt/kit'
import { defu } from 'defu'
import { joinURL } from 'ufo'
-import { genInterface } from 'knitwork'
import type { DeepRequired } from 'ts-essentials'
import type { NuxtModule } from 'nuxt/schema'
import { getOriginAndPathnameFromURL, isProduction } from './runtime/helpers'
@@ -23,6 +22,7 @@ import type {
RefreshHandler,
SupportedAuthProviders
} from './runtime/types'
+import { generateModuleTypes } from './runtime/utils/generateTypes'
const topLevelDefaults = {
isEnabled: true,
@@ -101,7 +101,10 @@ const PACKAGE_NAME = 'sidebase-auth'
export default defineNuxtModule({
meta: {
name: PACKAGE_NAME,
- configKey: 'auth'
+ configKey: 'auth',
+ compatibility: {
+ nuxt: '>=3.0.0'
+ }
},
setup(userOptions, nuxt) {
const logger = useLogger(PACKAGE_NAME)
@@ -178,26 +181,13 @@ export default defineNuxtModule({
inline: [resolve('./runtime')]
}
)
- nitroConfig.alias['#auth'] = resolve('./runtime/server/services')
+
+ nitroConfig.alias['#auth'] = resolve(`./runtime/server/services/${options.provider.type}`)
})
addTypeTemplate({
filename: 'types/auth.d.ts',
- getContents: () =>
- [
- '// AUTO-GENERATED BY @sidebase/nuxt-auth',
- 'declare module \'#auth\' {',
- ` const { getServerSession, getToken, NuxtAuthHandler }: typeof import('${resolve('./runtime/server/services')}')`,
- ...(options.provider.type === 'local'
- ? [genInterface(
- 'SessionData',
- (options.provider as any).session.dataType
- )]
- : []
- ),
- '}',
- ''
- ].join('\n')
+ getContents: () => generateModuleTypes(options.provider)
})
addTypeTemplate({
diff --git a/src/runtime/server/services/authjs/index.ts b/src/runtime/server/services/authjs/index.ts
new file mode 100644
index 00000000..f6ee14c7
--- /dev/null
+++ b/src/runtime/server/services/authjs/index.ts
@@ -0,0 +1 @@
+export { NuxtAuthHandler, getServerSession, getToken } from './nuxtAuthHandler'
diff --git a/src/runtime/server/services/index.ts b/src/runtime/server/services/index.ts
deleted file mode 100644
index 423ab845..00000000
--- a/src/runtime/server/services/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export { NuxtAuthHandler, getServerSession, getToken } from './authjs/nuxtAuthHandler'
diff --git a/src/runtime/server/services/local/getServerSession.ts b/src/runtime/server/services/local/getServerSession.ts
new file mode 100644
index 00000000..7147767a
--- /dev/null
+++ b/src/runtime/server/services/local/getServerSession.ts
@@ -0,0 +1,51 @@
+import { type H3Event, createError } from 'h3'
+import getURL from 'requrl'
+import { joinURL } from 'ufo'
+import { jsonPointerGet, useTypedBackendConfig } from '../../../helpers'
+import { getToken } from './getToken'
+
+// @ts-expect-error - #auth not defined
+import type { SessionData } from '#auth'
+import { useRuntimeConfig } from '#imports'
+
+function joinPathToApiURL(event: H3Event, path: string) {
+ const { origin, pathname, fullBaseUrl } = useRuntimeConfig().public.auth.computed
+
+ let baseURL
+ if (origin) {
+ // Case 1: An origin was supplied by the developer in the runtime-config. Use it by returning the already assembled full base url that contains it
+ baseURL = fullBaseUrl
+ }
+ else {
+ // Case 2: An origin was not supplied, we determine it from the request
+ const determinedOrigin = getURL(event.node.req, false)
+ baseURL = joinURL(determinedOrigin, pathname)
+ }
+
+ const base = path.startsWith('/') ? pathname : baseURL
+ return joinURL(base, path)
+}
+
+export async function getServerSession(event: H3Event): Promise {
+ const token = getToken(event)
+ if (!token) {
+ return null
+ }
+
+ const config = useTypedBackendConfig(useRuntimeConfig(), 'local')
+ const { path, method } = config.endpoints.getSession
+
+ // Compose heads to request the session
+ const headers = new Headers({ [config.token.headerName]: token } as HeadersInit)
+
+ try {
+ const url = joinPathToApiURL(event, path)
+ const result = await $fetch(url, { method, headers })
+ const { dataResponsePointer: sessionDataResponsePointer } = config.session
+ return jsonPointerGet(result, sessionDataResponsePointer)
+ }
+ catch (err) {
+ console.error(err)
+ throw createError({ statusCode: 401, statusMessage: 'Session could not be retrieved.' })
+ }
+}
diff --git a/src/runtime/server/services/local/getToken.ts b/src/runtime/server/services/local/getToken.ts
new file mode 100644
index 00000000..2a269607
--- /dev/null
+++ b/src/runtime/server/services/local/getToken.ts
@@ -0,0 +1,15 @@
+import { type H3Event, getCookie } from 'h3'
+import { useTypedBackendConfig } from '../../../helpers'
+import { formatToken } from '../../../utils/local'
+import { useRuntimeConfig } from '#imports'
+
+export function getToken(event: H3Event) {
+ const config = useTypedBackendConfig(useRuntimeConfig(), 'local')
+ const rawToken = getCookie(event, config.token.cookieName)
+ const token = formatToken(rawToken, config)
+
+ if (!token) {
+ return null
+ }
+ return token
+}
diff --git a/src/runtime/server/services/local/index.ts b/src/runtime/server/services/local/index.ts
new file mode 100644
index 00000000..d6d0f4b0
--- /dev/null
+++ b/src/runtime/server/services/local/index.ts
@@ -0,0 +1,2 @@
+export { getToken } from './getToken'
+export { getServerSession } from './getServerSession'
diff --git a/src/runtime/utils/generateTypes.ts b/src/runtime/utils/generateTypes.ts
new file mode 100644
index 00000000..7834fd3d
--- /dev/null
+++ b/src/runtime/utils/generateTypes.ts
@@ -0,0 +1,26 @@
+import { genInterface } from 'knitwork'
+import { createResolver } from '@nuxt/kit'
+import type { AuthProviders } from '../types'
+
+export function generateModuleTypes(provider: AuthProviders) {
+ const { resolve } = createResolver(import.meta.url)
+
+ const providerSpecificTypes: string[] = []
+
+ if (provider.type === 'authjs') {
+ providerSpecificTypes.push(` const { getServerSession, getToken, NuxtAuthHandler }: typeof import('${resolve('./runtime/server/services/authjs')}')`)
+ }
+
+ if (provider.type === 'local') {
+ providerSpecificTypes.push(` const { getServerSession, getToken }: typeof import('${resolve('./runtime/server/services/local')}')`)
+ providerSpecificTypes.push(genInterface('SessionData', (provider as any).session.dataType))
+ }
+
+ return [
+ '// AUTO-GENERATED BY @sidebase/nuxt-auth',
+ `declare module '#auth' {`,
+ ...providerSpecificTypes,
+ '}',
+ ''
+ ].join('\n')
+}