Nuxt 4 module providing authentication and API proxying for Transitland v2 applications. Replaces client-side Auth0 SPA token flow with server-side sessions via @auth0/auth0-nuxt using HTTP-only cookies.
- Server-side Auth0 sessions (always bundled; gracefully disabled at runtime when credentials are absent)
- Multi-backend API proxy at
/proxy/{backendName}/...with per-backend URL configuration - SSR auth header injection for
$fetchandglobalThis.fetch - Session enrichment with roles from a GraphQL
meendpoint - Composables:
useUser(),useLogin(),useLogout(),useApiEndpoint()
pnpm add @interline-io/tlv2-authPeer dependencies: nuxt, vue, h3
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@interline-io/tlv2-auth'],
runtimeConfig: {
// Server-side only (use NUXT_AUTH0_* / NUXT_TLV2_* env vars)
auth0: {
domain: '',
clientId: '',
clientSecret: '',
sessionSecret: '', // openssl rand -hex 32
appBaseUrl: '',
audience: '',
},
tlv2: {
graphqlApikey: '',
proxyBase: {
default: '', // e.g. https://transit.land/api/v2
},
},
public: {
tlv2: {
loginGate: false, // show login UI
requireLogin: false, // redirect unauthenticated users to login
},
},
},
})Auth0 is always installed at build time. The build-time presence of NUXT_AUTH0_CLIENT_ID determines the mode:
- No-auth (Playwright, local dev, CI rigs):
NUXT_AUTH0_CLIENT_IDunset → placeholders baked in, auth disabled at runtime, all users anonymous. - Live auth:
NUXT_AUTH0_CLIENT_IDset → realNUXT_AUTH0_*values read from env at runtime.
If credentials are supplied only at runtime, NUXT_AUTH0_CLIENT_ID must still be set at build time (any non-empty value works). Otherwise, placeholders will be baked in and runtime env vars will be silently ignored.
Options can be passed via the module array syntax:
modules: [['@interline-io/tlv2-auth', { autoAppBaseUrl: true }]]| Option | Type | Default | Description |
|---|---|---|---|
proxyEnabled |
boolean |
false |
Enable the API proxy |
proxyBase |
string | Record<string, string> |
— | Backend URL(s) for the API proxy |
requireLogin |
boolean |
false |
Redirect unauthenticated users to Auth0 login; also rejects unauthenticated proxy requests with 401 |
loginGate |
boolean |
false |
Show login UI gate |
authPrefix |
string |
'/auth' |
URL prefix for auth routes (login, logout, session) |
proxyPrefix |
string |
'/proxy' |
URL prefix for the proxy route |
autoAppBaseUrl |
boolean |
false |
Derive auth0 appBaseUrl from request Host header (see below) |
When enabled, the module derives appBaseUrl from the request's Host and x-forwarded-proto headers instead of using the static NUXT_AUTH0_APP_BASE_URL value. This is useful for branch/preview deploys where the URL isn't known at build time (e.g., Cloudflare Pages, Vercel preview deployments).
Caveat: This trusts the Host and x-forwarded-proto headers. Only enable on platforms where these are set by a trusted edge proxy (Cloudflare, Vercel, Netlify, etc.). Do not enable when the application is directly exposed to the internet without a trusted reverse proxy.
The module includes a synchronous Nitro plugin that works around a race condition in @auth0/auth0-nuxt, where its async server plugin doesn't complete before the first request on Cloudflare Workers. This runs automatically when auth0 is enabled and no-ops on platforms where the async plugin completes normally (e.g., Node.js).
The proxy at /proxy/{backendName}/... (configurable via proxyPrefix) forwards requests to the backend URL configured in runtimeConfig.tlv2.proxyBase.{backendName}.
- Unauthenticated requests get the server's default API key injected
- Authenticated requests additionally get the user's JWT
- Callers may provide their own API key via
?apikey=query param orapikeyheader, which takes precedence over the default - When
requireLoginistrue, unauthenticated proxy requests are rejected with 401
CSRF protection: This module does not include CSRF protection. The proxy injects server-side credentials on behalf of the user, so consuming applications should configure their own CSRF protection (e.g. nuxt-csurf) on proxy routes. This is especially important when requireLogin is false, as the proxy will forward requests with the server's API key for any caller. Note that nuxt-csurf only intercepts Nuxt's $fetch — if your app uses globalThis.fetch directly (e.g. Apollo), you will need a client plugin to inject the CSRF token on same-origin requests.
useUser()— returns current user state (loggedIn,id,name,email,roles,hasRole())useLogin(targetUrl)— redirects to Auth0 login, returns totargetUrlafteruseLogout()— redirects to Auth0 logoutuseApiEndpoint(path, backendName)— returns the correct endpoint URL (direct backend on server, proxy on client)
Composables are auto-imported by Nuxt, but explicit imports are recommended for type safety:
import { useUser, useApiEndpoint } from '@interline-io/tlv2-auth/composables'
import type { TlUser } from '@interline-io/tlv2-auth/composables'pnpm install # Install (requires NODE_AUTH_TOKEN for GitHub Packages)
pnpm dev # Start playground dev server (http://localhost:3000)
pnpm build # Build the module
pnpm test # Run unit tests
pnpm lint # ESLintCopy playground/.env.example to playground/.env and fill in your Auth0 and API credentials to test the full login flow.
Changesets drives versioning and publishing:
- PRs include a
.changeset/*.mdfile (created bypnpm changeset) - On merge to
main, the@changesets/actionbot opens or updates a "Version Packages" PR that bumps versions and generates CHANGELOGs - Merging the Version Packages PR triggers publish to GitHub Packages
Every push to main also publishes a SHA pre-release (0.0.0-sha.<sha>) for internal testing.
@auth0/auth0-nuxt— server-side Auth0 sessionsdefu— config merging