Auth with nuxt-auth-utils
nuxt-auth-utils keeps the session in a sealed cookie and you own the sign-in route, so this is the simplest setup: mint the PostgREST token in the same route that creates the session.
nuxt-auth-utils docs but isn't part of the playground.1. Configure the session and the JWT secret
Give the session a lifetime and use the same value for the token, so they end together:
export default defineNuxtConfig({
modules: ['nuxt-auth-utils', 'nuxt-postgrest'],
runtimeConfig: {
public: { postgrestUrl: 'http://localhost:3001' },
session: { maxAge: 60 * 60 * 24 }, // 1 day, in seconds
},
})
NUXT_SESSION_PASSWORD=a-random-string-of-at-least-32-characters
# Must equal PostgREST's jwt-secret (PGRST_JWT_SECRET)
NUXT_POSTGREST_JWT_SECRET=replace-with-a-secret-of-at-least-32-characters
2. Mint the token when the user signs in
export default defineEventHandler(async (event) => {
const user = await verifyCredentials(event) // your own check
await setUserSession(event, { user: { id: user.id, name: user.name } })
const token = await mintJwt(
{ role: 'authenticated', sub: user.id },
{ expiresIn: useRuntimeConfig(event).session.maxAge },
)
setPostgrestToken(event, token)
return { ok: true }
})
Signing in with an OAuth provider works the same way: do it in the handler's onSuccess, next to setUserSession.
export default defineOAuthGitHubEventHandler({
async onSuccess(event, { user }) {
await setUserSession(event, { user })
setPostgrestToken(event, await mintJwt({ role: 'authenticated', sub: String(user.id) }, { expiresIn: useRuntimeConfig(event).session.maxAge }))
return sendRedirect(event, '/')
},
})
3. Clear it when the user signs out
export default defineEventHandler(async (event) => {
await clearUserSession(event)
clearPostgrestToken(event)
return { ok: true }
})
4. Query as the user
Nothing special is needed. After signing in, usePostgrest() sends the token, including after client-side navigation:
<script setup lang="ts">
const { loggedIn, fetch: refreshSession } = useUserSession()
const { data: todos, refresh } = await useAsyncData('todos', async () => {
const { data, error } = await usePostgrest().from('todos').select('*')
if (error) throw error
return data
})
async function login() {
await $fetch('/api/login', { method: 'POST' })
await refreshSession()
await refresh() // run the query again, now as the user
}
</script>
In server routes, use usePostgrestUser(event) the same way. Your RLS policies see the claims you minted, so a policy like owner = current_setting('request.jwt.claims', true)::json ->> 'sub' matches the signed-in user. See Reading claims in RLS policies.
Things to know
- Fixed lifetime. The session and the token both last
session.maxAge, and neither is extended while the user is active. If you refresh sessions yourself, mint a new token at the same time. See Expiry. - Revocation. Clearing the session doesn't invalidate a token a user already copied. Keep
maxAgeas short as your app can stand.