H3 and Nitro Setup
The Nuxt module handles all wiring automatically, but auth-h3client also works as a standalone library with plain H3 or Nitro servers. The public API is identical across H3 v1 and v2: the same function names, the same configuration object, the same handler wrappers. The differences are in how H3 itself registers middleware and defines handlers.
Import paths
| Import | H3 version | Notes |
|---|---|---|
auth-h3client or auth-h3client/v1 | H3 v1 (1.15.x) | Default, backwards compatible |
auth-h3client/v2 | H3 v2 (2.0 beta) | Opt-in for H3 v2 projects |
auth-h3client/client | N/A | Vue/Nuxt composables only |
Configuration
Call configuration() exactly once when your server starts. This sets the global configuration used by every handler and middleware in the library.
import { configuration } from 'auth-h3client/v1'
import { createStorage } from 'unstorage'
import memoryDriver from 'unstorage/drivers/memory'
const storage = createStorage({ driver: memoryDriver() })
configuration({
server: {
auth_location: {
serverOrDNS: process.env.AUTH_HOST || 'localhost',
port: Number(process.env.AUTH_PORT) || 4000,
},
hmac: {
enableHmac: true,
clientId: process.env.HMAC_CLIENT_ID!,
sharedSecret: process.env.HMAC_SHARED_SECRET!,
},
ssl: { enableSSL: false },
cryptoCookiesSecret: process.env.AUTH_CRYPTO_COOKIES!,
},
uStorage: {
storage,
cacheOptions: { successTtl: 60 * 60 * 24 * 30, rateLimitTtl: 10 },
},
onSuccessRedirect: 'https://app.example.com/dashboard',
enableFireWallBans: false,
logLevel: 'info',
})
See Configuration for the complete reference of all options.
H3 v1 setup
import { createApp, createRouter } from 'h3'
import {
configuration,
httpLogger,
isIPValid,
botDetectorMiddleware,
generateCsrfCookie,
useAuthRoutes,
magicLinksRouter,
useOAuthRoutes,
bounceRouter,
getAuthStatusHandler,
} from 'auth-h3client/v1'
getApiListsController,
configuration({ /* ... */ })
const app = createApp()
// Global middleware (order matters)
httpLogger()(app) // HTTP request logging
app.use(isIPValid) // IP extraction and validation
app.use(botDetectorMiddleware) // Bot detection via IAM /check
app.use(generateCsrfCookie) // CSRF cookie issuance
// Routes
const router = createRouter()
useAuthRoutes(router) // POST /login, /signup, /logout
useOAuthRoutes(router) // GET /oauth/:provider, /oauth/callback/:provider
bounceRouter(router) // GET /auth/bounce
magicLinksRouter(router, 'api') // MFA, password reset, email change
// Auth status endpoint
router.get('/auth/users/authStatus', getAuthStatusHandler)
// Optional API token inventory endpoint
router.get('/api/auth/api-tokens', getApiListsController)
app.use(router)
isIPValid, botDetectorMiddleware, and generateCsrfCookie
chain is for browser session routes. Do not mount that chain in front of
routes that use defineAuthenticatePublicApi, because repeated
machine-to-machine X-API-KEY verification traffic can hit the bot detector
and escalate into rate limits or bans. Keep the chain for regular auth routes,
getApiListsController, and defineApiManagementHandler, and add a path
guard if you need both kinds of routes in the same app.Route-level middleware in v1
import { defineEventHandler } from 'h3'
import { verifyCsrfCookie, contentType, limitBytes, signUpHandler, loginHandler, logoutHandler } from 'auth-h3client/v1'
export function useAuthRoutes(router: Router) {
const signUpPipeline = defineEventHandler(async (event) => {
await verifyCsrfCookie(event);
await contentType('application/json')(event);
await limitBytes(1024)(event);
return signUpHandler(event);
})
router.post('/signup', signUpPipeline);
const loginPipeline = defineEventHandler(async (event) => {
await verifyCsrfCookie(event);
await contentType('application/json')(event);
await limitBytes(1024)(event);
return loginHandler(event);
});
router.post('/login', loginPipeline);
const logoutPipeline = defineEventHandler(async (event) => {
await verifyCsrfCookie(event);
await limitBytes(0)(event);
return logoutHandler(event);
})
router.post('/logout', logoutPipeline)
}
H3 v2 setup
import { H3 } from 'h3'
import {
configuration,
httpLogger,
isIPValid,
botDetectorMiddleware,
generateCsrfCookie,
useAuthRoutes,
magicLinksRouter,
useOAuthRoutes,
bounceRouter,
getAuthStatusHandler,
getApiListsController,
} from 'auth-h3client/v2'
configuration({ /* ... */ })
const app = new H3()
app.register(httpLogger()) // v2 logger is a plugin: register directly
app.use(isIPValid) // IP extraction and validation
app.use(botDetectorMiddleware) // Bot detection via IAM /check
app.use(generateCsrfCookie) // CSRF cookie issuance
// Routes
useAuthRoutes(app); // POST /login, /signup, /logout
useOAuthRoutes(app); // GET /oauth/:provider, /oauth/callback/:provider
bounceRouter(app); // GET /auth/bounce
magicLinksRouter(app, 'api'); // MFA, password reset, email change
// Auth status endpoint
app.get('/auth/users/authStatus', getAuthStatusHandler)
// Optional API token inventory endpoint
app.get('/api/auth/api-tokens', getApiListsController)
// Your handlers
Optional API token inventory route
getApiListsController gives you a ready-made GET endpoint for authenticated
API token inventory reads. It runs the authenticated session pipeline, calls
IAM /api/manage/list-metadata, and strips public_identifier out of every
token before returning the payload.
If you also need write operations such as token creation, rotation, revocation,
IP restriction updates, or privilege changes, build your POST routes with
defineApiManagementHandler. For public machine-to-machine routes protected by
X-API-KEY, use defineAuthenticatePublicApi. Both wrappers are documented in
the Middleware Reference.
Route-level middleware in v2
H3 v2 passes middleware as the third argument to route methods:
import { defineHandler } from 'h3'
import { verifyCsrfCookie, contentType, limitBytes, signUpHandler, loginHandler, logoutHandler } from 'auth-h3client/v2'
export function useAuthRoutes(router: H3) {
router.post('/signup', signUpHandler,
{ middleware: [verifyCsrfCookie, contentType('application/json'), limitBytes(1024)] },
)
router.post('/logout', logoutHandler,
{middleware: [verifyCsrfCookie, limitBytes(0)]}
)
router.post('/login', loginHandler,
{middleware: [verifyCsrfCookie, contentType('application/json'), limitBytes(1024)]}
)
}
Key differences between v1 and v2
| Aspect | H3 v1 | H3 v2 |
|---|---|---|
| Handler definition | defineEventHandler(...) | defineHandler(...) |
| Route middleware | router.use('/path', mw) chained | { middleware: [mw1, mw2] } as third arg |
| HTTP logger | httpLogger()(app) (function call) | app.register(httpLogger()) (plugin) |
| Redirect | sendRedirect(event, url) | redirect(event, url) |
| Errors | createError({ ... }) | throw new HTTPError({ ... }) |
| Response status | setResponseStatus(event, code) | event.res.status = code |
The exported handler wrappers (defineAuthenticatedEventHandler, defineVerifiedCsrfHandler, etc.), route registrars, and utility functions have the same names and signatures in both versions.
Writing custom handlers
When writing your own handlers alongside the library's built-in routes:
H3 v1
import { defineEventHandler } from 'h3'
import { defineAuthenticatedEventHandler } from 'auth-h3client/v1'
router.get('/api/profile', defineAuthenticatedEventHandler(async (event) => {
const { userId, roles } = event.context.authorizedData
return { userId, roles }
}))
H3 v2
import { defineHandler } from 'h3'
import { defineAuthenticatedEventHandler } from 'auth-h3client/v2'
router.get('/api/profile', defineAuthenticatedEventHandler(async (event) => {
const { userId, roles } = event.context.authorizedData
return { userId, roles }
}))
The handler wrappers internally use the correct defineEventHandler or defineHandler from the version you import.
Nitro plugin setup
When using Nitro (without the full Nuxt module), configure the library in a Nitro plugin:
import { defineNitroPlugin } from "nitro";
import { configuration, httpLogger, useAuthRoutes, useOAuthRoutes, magicLinksRouter, bounceRouter } from 'auth-h3client';
import { configDefaults } from 'auth-h3client/server/templates'
export default defineNitroPlugin((nitro) => {
configuration({
...configDefaults,
uStorage: {
storage: useStorage('cache'),
cacheOptions: {
successTtl: 60 * 60 * 24 * 30,
rateLimitTtl: 10
}
}
});
httpLogger()(nitro.h3App);
nitro.h3App.use(isIPValid)
nitro.h3App.use(botDetectorMiddleware)
nitro.h3App.use(generateCsrfCookie)
useAuthRoutes(nitro.router);
useOAuthRoutes(nitro.router);
bounceRouter(nitro.router);
magicLinksRouter(nitro.router, 'api');
})
auth-h3client/v2configuration needs to be called ones, in your app entry point, the middlewares needs to be registered along side the routes in this exact order.What the Nuxt module adds
If you use the Nuxt module instead of manual setup, it provides:
- Auto-imports of all server utilities in the
server/directory - Auto-imports of client composables (
useAuthData,executeRequest,getCsrfToken,useMagicLink) - Automatic registration of the auth status route at the configured
authStatusUrl - Optional registration of an API token list route at
registerApiRoute.path - The
enableMiddlewareoption for global middleware activation only. Disabling it does not disable the auth status route or the optional API token list route.
See Nuxt Module for the Nuxt module setup.