Skip to content

Commit 5306fd8

Browse files
authored
docs: improve published auth skill (#406)
1 parent 4f691eb commit 5306fd8

6 files changed

Lines changed: 144 additions & 93 deletions

File tree

docs/public/.well-known/skills/index.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"skills": [{
33
"name": "nuxt-better-auth",
4-
"description": "Use when implementing auth in Nuxt apps with @nuxtjs/better-auth - provides client composables, server helpers, route protection, session refresh helpers, and Better Auth plugin integration.",
4+
"description": "Guides authentication in Nuxt apps using @nuxtjs/better-auth. Use when installing or configuring the module, using its client or server APIs, protecting routes, refreshing sessions, or integrating Better Auth plugins.",
55
"files": [
66
"SKILL.md",
77
"references/client-auth.md",
Lines changed: 21 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,99 +1,37 @@
11
---
22
name: nuxt-better-auth
3-
description: Use when implementing auth in Nuxt apps with @nuxtjs/better-auth - provides client composables, server helpers, route protection, session refresh helpers, and Better Auth plugin integration.
3+
description: Guides authentication in Nuxt apps using @nuxtjs/better-auth. Use when installing or configuring the module, using its client or server APIs, protecting routes, refreshing sessions, or integrating Better Auth plugins.
44
license: MIT
55
---
66

77
# Nuxt Better Auth
88

9-
Authentication module for Nuxt built on [Better Auth](https://www.better-auth.com/). It adds Nuxt-specific setup, route protection, server helpers, and typed auth state.
9+
Use the smallest reference that matches the task.
1010

11-
> Alpha status: the package is still pre-stable. Verify behavior against the current docs and source before relying on edge cases.
11+
## Pick a reference
1212

13-
## When to Use
13+
| Task | Read |
14+
| --- | --- |
15+
| Install the module, configure environment variables, or create config files | [references/installation.md](references/installation.md) |
16+
| Read client session state, build auth forms, call plugins, or fetch auth-bound data | [references/client-auth.md](references/client-auth.md) |
17+
| Read or enforce sessions in server handlers, create sessions, or refresh cached session data | [references/server-auth.md](references/server-auth.md) |
18+
| Protect pages or API routes and configure redirects | [references/route-protection.md](references/route-protection.md) |
19+
| Register Better Auth plugins and their client companions | [references/plugins.md](references/plugins.md) |
20+
| Configure NuxtHub, generated schema, or secondary storage | [references/database.md](references/database.md) |
21+
| Connect a Nuxt frontend to an external Better Auth server | [references/client-only.md](references/client-only.md) |
22+
| Use inferred auth types or add project fields | [references/types.md](references/types.md) |
1423

15-
- Installing/configuring `@nuxtjs/better-auth`
16-
- Implementing sign-in, sign-up, sign-out, or custom auth flows
17-
- Protecting routes (client and server)
18-
- Accessing user session in API routes
19-
- Refreshing session state after custom auth endpoints or server-side session changes
20-
- Integrating Better Auth plugins (admin, passkey, 2FA)
21-
- Setting up database with NuxtHub
22-
- Using clientOnly mode for external auth backends
24+
## Rules that apply across tasks
2325

24-
## Available Guidance
26+
- Confirm the consumer's installed package version supports each API you use. The published skill may be newer than the consumer's lockfile.
27+
- Enforce protected API reads and mutations server-side with `requireUserSession(event)`. Route rules and page meta also control navigation.
28+
- Navigate only to validated local redirect paths.
29+
- In `clientOnly` mode, do not use local server helpers or expect SSR session hydration.
30+
- After server-side changes to session payload fields, call `refreshSessionCookieCache(event)`. Wrap custom client auth endpoints that create or change the current session with `runWithSessionRefresh()`.
2531

26-
| File | Topics |
27-
| -------------------------------------------------------------------- | ---------------------------------------------------------------------- |
28-
| **[references/installation.md](references/installation.md)** | install flow, env vars, config files |
29-
| **[references/client-auth.md](references/client-auth.md)** | `useUserSession`, client methods, redirects, loading states, custom actions |
30-
| **[references/server-auth.md](references/server-auth.md)** | `serverAuth`, session helpers, `refreshSessionCookieCache`, API enforcement |
31-
| **[references/route-protection.md](references/route-protection.md)** | route rules, page meta, API protection |
32-
| **[references/plugins.md](references/plugins.md)** | plugin pairing between server and client |
33-
| **[references/database.md](references/database.md)** | NuxtHub schema generation, secondary storage |
34-
| **[references/client-only.md](references/client-only.md)** | external Better Auth backends and `clientOnly` mode |
35-
| **[references/types.md](references/types.md)** | public auth types and augmentation |
36-
37-
## Usage Pattern
38-
39-
- Installing module? → [references/installation.md](references/installation.md)
40-
- Login/signup forms? → [references/client-auth.md](references/client-auth.md)
41-
- API route protection? → [references/server-auth.md](references/server-auth.md)
42-
- Route rules/page meta? → [references/route-protection.md](references/route-protection.md)
43-
- Using plugins? → [references/plugins.md](references/plugins.md)
44-
- Database setup? → [references/database.md](references/database.md)
45-
- External auth backend? → [references/client-only.md](references/client-only.md)
46-
- TypeScript types? → [references/types.md](references/types.md)
47-
48-
Do not load every reference file by default. Pick the smallest file that matches the task.
49-
50-
## Key Concepts
51-
52-
| Concept | Description |
53-
| ---------------------- | --------------------------------------------------------------- |
54-
| `useUserSession()` | Client composable - user, session, loggedIn, refresh, sign-out |
55-
| `runWithSessionRefresh()` | Refresh local session after custom auth endpoints |
56-
| `requireUserSession()` | Server helper - throws 401/403 if not authenticated |
57-
| `refreshSessionCookieCache()` | Refresh Better Auth's cached session cookie after server updates |
58-
| `auth` route mode | `'user'`, `'guest'`, `{ user: {...} }`, or `false` |
59-
| `serverAuth()` | Get Better Auth instance in server routes |
60-
61-
## Quick Reference
62-
63-
```ts
64-
// Client: useUserSession()
65-
const { user, loggedIn, signIn, signOut } = useUserSession()
66-
await signIn.email({ email, password }, { onSuccess: () => navigateTo('/') })
67-
```
68-
69-
```ts
70-
// Client: custom auth endpoint
71-
await runWithSessionRefresh(() => $fetch('/api/custom-login', { method: 'POST', body }))
72-
```
73-
74-
```ts
75-
// Server: requireUserSession()
76-
const { user } = await requireUserSession(event, { user: { role: 'admin' } })
77-
```
78-
79-
```ts
80-
// Server: after updating fields returned by session helpers
81-
await updateCurrentUser(event)
82-
await refreshSessionCookieCache(event)
83-
```
84-
85-
```ts
86-
// nuxt.config.ts: Route protection
87-
routeRules: {
88-
'/admin/**': { auth: { user: { role: 'admin' } } },
89-
'/login': { auth: 'guest' },
90-
'/app/**': { auth: 'user' }
91-
}
92-
```
93-
94-
Broad rules such as `'/**': { auth: 'user' }` skip framework/module internals like `/_nuxt/**`, `/_ipx/**`, `/api/auth/**`, `/api/_better-auth/**`, and `/api/_nuxt_icon/**`.
32+
Before finishing an implementation, run the consumer's typecheck and the narrowest relevant auth test.
9533

9634
## Resources
9735

9836
- [Documentation site](https://better-auth.nuxt.dev)
99-
- [Better Auth Docs](https://www.better-auth.com/)
37+
- [Better Auth docs](https://www.better-auth.com/)

docs/public/.well-known/skills/nuxt-better-auth/references/client-auth.md

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
# Client-side authentication
22

3-
## Primary entry points
3+
## Choose an API
4+
5+
| Need | API |
6+
| --- | --- |
7+
| Session state and lifecycle actions | `useUserSession()` |
8+
| Direct Better Auth client or plugin methods | `useAuthClient()` |
9+
| Sign-in or sign-up form state | `useSignIn()` or `useSignUp()` |
10+
| Action state for a client or plugin method | `useAuthClientAction()` |
11+
| Action state for any async function | `useAction()` |
12+
| Refresh session state after a custom auth endpoint | `runWithSessionRefresh()` |
13+
| Request-scoped auth fetch with typed endpoints | `useAuthRequestFetch()` |
14+
| SSR-safe auth-bound data | `useAuthAsyncData()` |
15+
16+
`useUserSessionState()` is a deprecated alias for `useUserSession()`.
17+
18+
## Session state and direct client access
419

520
```ts
621
const { user, session, loggedIn, ready, fetchSession, signOut, updateUser } = useUserSession()
722
const client = useAuthClient()
823
```
924

10-
`useUserSession()` is the store-safe session API. It returns auth state plus session lifecycle actions. It does not expose raw Better Auth client namespaces.
11-
12-
Use `useAuthClient()` when you need direct Better Auth client/plugin methods. It returns the client in the browser and `null` during SSR.
25+
`useUserSession()` is safe to return from a Pinia setup store. `useAuthClient()` returns the client in the browser and `null` during SSR.
1326

1427
## Sign-in and sign-up forms
1528

@@ -40,6 +53,8 @@ const openPortal = useAuthClientAction(client => client.customer.portal)
4053
await openPortal.execute()
4154
```
4255

56+
Use `useAction()` for the same action state around an arbitrary async function.
57+
4358
## Custom auth endpoints
4459

4560
Use `runWithSessionRefresh()` around custom endpoints that create or change the current session.
@@ -55,6 +70,26 @@ await runWithSessionRefresh(() =>
5570

5671
The helper awaits your request, then refreshes local session state unless the result is a Better Auth action error result.
5772

73+
## Auth-bound data
74+
75+
Use `useAuthRequestFetch()` for low-level request control. During SSR it forwards the request context, including cookies.
76+
77+
The endpoint below comes from a configured custom customer plugin. Replace it with an endpoint generated by the consumer's Better Auth config.
78+
79+
```ts
80+
const requestFetch = useAuthRequestFetch()
81+
const customerState = await requestFetch('/api/auth/customer/state')
82+
```
83+
84+
Use `useAuthAsyncData()` for keyed SSR data with Nuxt loading and error state. It requires an authenticated user by default and resolves to `null` without calling the endpoint when the user is signed out.
85+
86+
```ts
87+
const { data, pending, error } = await useAuthAsyncData(
88+
'customer-state',
89+
requestFetch => requestFetch('/api/auth/customer/state'),
90+
)
91+
```
92+
5893
## Force refresh
5994

6095
```ts

docs/public/.well-known/skills/nuxt-better-auth/references/installation.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,20 @@ Required files:
1818
NUXT_BETTER_AUTH_SECRET=replace-with-a-random-32-character-secret
1919
```
2020

21-
Optional but commonly required in production:
21+
Set the public site URL when the deployment platform cannot detect it:
2222

2323
```ini
2424
NUXT_PUBLIC_SITE_URL=https://your-domain.com
2525
```
2626

2727
`BETTER_AUTH_SECRET` is still accepted as a fallback. Prefer `NUXT_BETTER_AUTH_SECRET`.
2828

29+
For non-destructive secret rotation, keep the current and previous secrets in Better Auth's versioned variable:
30+
31+
```ini
32+
BETTER_AUTH_SECRETS=2:current-secret-must-be-at-least-32-characters,1:previous-secret-must-be-at-least-32-characters
33+
```
34+
2935
## Minimal module setup
3036

3137
```ts
@@ -54,7 +60,7 @@ import { defineClientAuth } from '@nuxtjs/better-auth/config'
5460
export default defineClientAuth({})
5561
```
5662

57-
## Important rules
63+
## Module-owned values
5864

5965
- Do not set `secret` manually in `defineServerAuth()`. The module injects it.
6066
- Do not set `baseURL` manually in full mode. The module resolves it.

docs/public/.well-known/skills/nuxt-better-auth/references/route-protection.md

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ Use route rules and page meta for navigation UX. Use `requireUserSession(event)`
1313
```ts
1414
export default defineNuxtConfig({
1515
routeRules: {
16-
'/app/**': { auth: 'user' },
17-
'/login': { auth: 'guest' },
18-
'/admin/**': { auth: { user: { role: 'admin' } } },
16+
'/app/**': { auth: { only: 'user', redirectTo: '/login' } },
17+
'/login': { auth: { only: 'guest', redirectTo: '/app' } },
18+
'/admin/**': { auth: { only: 'user', user: { role: 'admin' } } },
1919
},
2020
})
2121
```
@@ -31,6 +31,29 @@ The same auth keys work under `nitro.routeRules`. If both `routeRules` and `nitr
3131
- multiple fields mean AND matching
3232
- `false`: disable auth for that route/page
3333

34+
The string forms remain available as shorthand. `auth: 'user'` redirects to the configured login fallback, and `auth: 'guest'` redirects to the configured guest fallback.
35+
36+
## Redirects
37+
38+
```ts
39+
export default defineNuxtConfig({
40+
auth: {
41+
redirects: {
42+
login: '/login',
43+
guest: '/',
44+
authenticated: '/app',
45+
logout: '/goodbye',
46+
},
47+
preserveRedirect: true,
48+
redirectQueryKey: 'redirect',
49+
},
50+
})
51+
```
52+
53+
- Per-route `redirectTo` takes precedence over `auth.redirects.login` and `auth.redirects.guest`.
54+
- A validated local redirect query takes precedence over `auth.redirects.authenticated` after sign-in or sign-up.
55+
- `auth.redirects.logout` applies after sign-out unless the caller supplies `onSuccess`.
56+
3457
## Broad rules and internals
3558

3659
Broad rules such as `'/**': { auth: 'user' }` intentionally skip framework and module internals that must stay reachable:

test/public-skill.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
2+
import { dirname, join, relative, resolve } from 'node:path'
3+
import { describe, expect, it } from 'vitest'
4+
import yaml from 'yaml'
5+
6+
const bundleRoot = resolve('docs/public/.well-known/skills')
7+
const skillRoot = join(bundleRoot, 'nuxt-better-auth')
8+
9+
function listFiles(dir: string): string[] {
10+
return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
11+
const path = join(dir, entry.name)
12+
return entry.isDirectory() ? listFiles(path) : [relative(skillRoot, path)]
13+
}).sort()
14+
}
15+
16+
describe('public skill bundle', () => {
17+
it('keeps its manifest, frontmatter, links, and code fences valid', () => {
18+
const manifest = JSON.parse(readFileSync(join(bundleRoot, 'index.json'), 'utf8'))
19+
const skill = manifest.skills.find((entry: { name: string }) => entry.name === 'nuxt-better-auth')
20+
const skillMarkdown = readFileSync(join(skillRoot, 'SKILL.md'), 'utf8')
21+
const frontmatterMatch = skillMarkdown.match(/^---\n([\s\S]*?)\n---/)
22+
23+
expect(skill).toBeDefined()
24+
expect(frontmatterMatch).not.toBeNull()
25+
if (!skill || !frontmatterMatch)
26+
return
27+
28+
const frontmatter = yaml.parse(frontmatterMatch[1])
29+
expect(skill.name).toBe(frontmatter.name)
30+
expect(skill.description).toBe(frontmatter.description)
31+
expect([...skill.files].sort()).toEqual(listFiles(skillRoot))
32+
33+
for (const file of skill.files as string[]) {
34+
const path = join(skillRoot, file)
35+
const contents = readFileSync(path, 'utf8')
36+
const fences = contents.match(/^```/gm) ?? []
37+
38+
expect(fences.length % 2, `${file} has an unclosed code fence`).toBe(0)
39+
40+
for (const match of contents.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
41+
const target = match[1]
42+
if (/^(?:https?:|#)/.test(target))
43+
continue
44+
45+
expect(existsSync(resolve(dirname(path), target)), `${file} links to missing ${target}`).toBe(true)
46+
}
47+
}
48+
})
49+
})

0 commit comments

Comments
 (0)