Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 74 additions & 11 deletions docs/content/1.getting-started/2.configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,18 +149,35 @@ Define your authentication logic in `server/auth.config.ts`, including plugins,

Use the `defineServerAuth` helper to ensure type safety and access context. It accepts an object or function syntax.

```ts [server/auth.config.ts]
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
```ts twoslash [server/auth.config.ts]
interface ServerAuthContext {
db: unknown
runtimeConfig: {
public: {
siteUrl?: string
}
}
}

type ServerAuthConfig = {
emailAndPassword?: {
enabled: boolean
}
}

declare function defineServerAuth<T extends ServerAuthConfig>(
config: T | ((ctx: ServerAuthContext) => T)
): (ctx: ServerAuthContext) => T

// Object syntax (simplest)
export default defineServerAuth({
const objectSyntax = defineServerAuth({
emailAndPassword: { enabled: true }
})

// Function syntax (access context)
export default defineServerAuth((ctx) => ({
const functionSyntax = defineServerAuth((ctx) => ({
emailAndPassword: { enabled: true }
}))

export default functionSyntax
```

::note
Expand All @@ -173,8 +190,26 @@ The module automatically injects `secret` and `baseURL`. You don't need to confi

When using the function syntax, `defineServerAuth` callback receives a context object with useful properties:

```ts [server/auth.config.ts]
import { defineServerAuth } from '@onmax/nuxt-better-auth/config'
```ts twoslash [server/auth.config.ts]
interface ServerAuthContext {
db: unknown
runtimeConfig: {
public: {
siteUrl?: string
}
}
}

type ServerAuthConfig = {
appName?: string
emailAndPassword?: {
enabled: boolean
}
}

declare function defineServerAuth<T extends ServerAuthConfig>(
config: T | ((ctx: ServerAuthContext) => T)
): (ctx: ServerAuthContext) => T

export default defineServerAuth((ctx) => ({
emailAndPassword: { enabled: true },
Expand Down Expand Up @@ -262,9 +297,37 @@ export default defineNuxtModule({

Access sessions from server handlers:

```ts
const { user, session } = await getUserSession(event)
if (!user) throw createError({ statusCode: 401 })
```ts twoslash [server/api/session.get.ts]
interface H3Event {
headers: HeadersInit
}

interface AuthUser {
email: string
id: string
}

interface AuthSession {
id: string
}

interface AppSession {
session: AuthSession
user: AuthUser
}

declare function defineEventHandler<T>(
handler: (event: H3Event) => T
): (event: H3Event) => T

declare function getUserSession(event: H3Event): Promise<AppSession | null>

export default defineEventHandler(async (event) => {
const { user, session } = await getUserSession(event) ?? {}
// ^?

return { user, session }
})
```

::callout{icon="i-lucide-arrow-right" to="/getting-started/client-setup"}
Expand Down
42 changes: 31 additions & 11 deletions docs/content/1.getting-started/3.client-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,27 @@ Create `app/auth.config.ts` with a default export using `defineClientAuth`.

The `defineClientAuth` helper supports two syntaxes: an object for simple configurations, or a function when you need access to context like the resolved site URL.

```ts [app/auth.config.ts]
import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
```ts twoslash [app/auth.config.ts]
interface ClientAuthContext {
siteUrl: string
}

type ClientAuthConfig = Record<string, unknown>

// Object syntax (simplest)
export default defineClientAuth({})
declare function defineClientAuth<T extends ClientAuthConfig>(
config: T | ((ctx: ClientAuthContext) => T)
): (baseURL: string) => T

// Function syntax (access context)
export default defineClientAuth(({ siteUrl }) => ({
// siteUrl contains the resolved base URL
}))
const objectSyntax = defineClientAuth({})

const functionSyntax = defineClientAuth(({ siteUrl }) => {
siteUrl
// ^?

return {}
})

export default functionSyntax
```

::note
Expand All @@ -30,9 +41,18 @@ The helper creates a factory function that the module calls with the correct `ba

If you added a plugin in your server config (`server/auth.config.ts`), make sure to add its client equivalent here.

```ts [app/auth.config.ts]
import { defineClientAuth } from '@onmax/nuxt-better-auth/config'
import { adminClient } from 'better-auth/client/plugins'
```ts twoslash [app/auth.config.ts]
type ClientAuthConfig = {
plugins?: unknown[]
}

declare function defineClientAuth<T extends ClientAuthConfig>(
config: T
): (baseURL: string) => T

declare function adminClient(): {
name: 'admin'
}

export default defineClientAuth({
plugins: [
Expand Down
13 changes: 12 additions & 1 deletion docs/content/1.getting-started/4.type-augmentation.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,19 @@ export default defineServerAuth({

You can immediately access the `role` property in your Vue components:

```vue
```vue twoslash [pages/dashboard.vue]
<script setup lang="ts">
type AuthUser = {
name?: string
role?: 'admin' | 'user'
}

declare function useUserSession(): {
user: {
value: AuthUser | null
}
}

const { user } = useUserSession()

// user.value.role is fully typed!
Expand Down
31 changes: 30 additions & 1 deletion docs/content/2.core-concepts/2.sessions.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,36 @@ title: Sessions
description: Access reactive session state with SSR support using `useUserSession()`.
---

```ts [pages/dashboard.vue]
```ts twoslash [pages/dashboard.vue]
type Ref<T> = { value: T }
type ComputedRef<T> = { readonly value: T }

interface AuthUser {
email: string
id: string
}

interface AuthSession {
id: string
}

interface UseUserSessionReturn {
fetchSession: () => Promise<void>
loggedIn: ComputedRef<boolean>
ready: ComputedRef<boolean>
session: Ref<AuthSession | null>
signIn: {
email: (input: { email: string, password: string }) => Promise<void>
}
signOut: () => Promise<void>
signUp: {
email: (input: { email: string, password: string }) => Promise<void>
}
user: Ref<AuthUser | null>
}

declare function useUserSession(): UseUserSessionReturn

const {
user,
session,
Expand Down
75 changes: 69 additions & 6 deletions docs/content/2.core-concepts/3.route-protection.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,36 @@ definePageMeta({

Protecting your API endpoints is critical. Use `requireUserSession` to enforce authentication on server routes.

```ts [server/api/secret.get.ts]
```ts twoslash [server/api/secret.get.ts]
interface H3Event {
headers: HeadersInit
}

interface AuthUser {
email: string
id: string
}

interface AuthSession {
id: string
}

interface AppSession {
session: AuthSession
user: AuthUser
}

declare function defineEventHandler<T>(
handler: (event: H3Event) => T
): (event: H3Event) => T

declare function requireUserSession(event: H3Event): Promise<AppSession>

export default defineEventHandler(async (event) => {
// Throws 401 if not logged in
const { user } = await requireUserSession(event)

// ^?

return { secret: 'data' }
})
```
Expand Down Expand Up @@ -202,10 +227,48 @@ Always validate redirect URLs. Accepting arbitrary URLs allows attackers to redi

For complex authorization logic:

```ts [server/api/admin/report.ts]
const session = await requireUserSession(event, {
user: { emailVerified: true },
rule: ({ user }) => user.subscriptionActive
```ts twoslash [server/api/admin/report.ts]
interface H3Event {
headers: HeadersInit
}

interface AuthUser {
email: string
emailVerified?: boolean
id: string
subscriptionActive?: boolean
}

interface AuthSession {
id: string
}

interface AppSession {
session: AuthSession
user: AuthUser
}

interface RequireSessionOptions {
rule?: (ctx: { session: AuthSession, user: AuthUser }) => boolean | Promise<boolean>
user?: Partial<AuthUser>
}

declare function defineEventHandler<T>(
handler: (event: H3Event) => T
): (event: H3Event) => T

declare function requireUserSession(
event: H3Event,
options?: RequireSessionOptions
): Promise<AppSession>

export default defineEventHandler(async (event) => {
const session = await requireUserSession(event, {
user: { emailVerified: true },
rule: ({ user }) => user.subscriptionActive === true,
})

return session
})
```

Expand Down
32 changes: 29 additions & 3 deletions docs/content/3.guides/1.role-based-access.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,41 @@ For complex authorization, use custom middleware or `requireUserSession` with a

For authorization logic that can't be expressed with field matching:

```ts [server/api/reports.ts]
```ts twoslash [server/api/reports.ts]
interface H3Event {
headers: HeadersInit
}

interface AuthUser {
id: string
permissions?: string[]
}

interface AuthSession {
id: string
}

interface RequireSessionOptions {
rule?: (ctx: { session: AuthSession, user: AuthUser }) => boolean | Promise<boolean>
}

declare function defineEventHandler<T>(
handler: (event: H3Event) => T
): (event: H3Event) => T

declare function requireUserSession(
event: H3Event,
options?: RequireSessionOptions
): Promise<{ session: AuthSession, user: AuthUser }>

export default defineEventHandler(async (event) => {
await requireUserSession(event, {
rule: (session) => {
// User must have 'reports:read' permission
return session.user.permissions?.includes('reports:read')
return session.user.permissions?.includes('reports:read') === true
}
})

return getReports()
return []
})
```
Loading
Loading