Nuxt Module

Nuxt module setup, plugin configuration, and first-run verification for the Auth H3 Client.

The @riavzon/auth-h3client module handles authentication for Nuxt applications. It provides a module that auto-imports client-side composables and server-side utilities, plus optional security middleware.

Requirements

  • Node.js 22 or later
  • Nuxt 3 and later or a Nitro/H3 application
  • A running IAM service reachable from your server

Installation

pnpm add @riavzon/auth-h3client

Nuxt module setup

Register the module in nuxt.config.ts. This enables the auto-import of all server utilities and client composables, and optionally registers the global security middleware.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['auth-h3client/module'],
  authH3Client: {
    enableMiddleware: true,
    authStatusUrl: '/api/auth/users/authStatus',
    registerApiRoute: {
      path: '/api/auth/api-tokens'
    }
  }
})

Module options

enableMiddleware
boolean
Default to true - Registers a global server middleware that runs on every incoming request. The middleware calls IP validation, bot detection, and CSRF cookie generation in sequence for browser auth flows. Set to false if you want to provide your own middleware so you can exclude machine-to-machine routes protected by defineAuthenticatePublicApi. This only disables the bundled middleware. The module still registers the auth status route and any configured registerApiRoute handler.
authStatusUrl
string
Default to "/api/auth/users/authStatus" - The route path for the auth status endpoint. The module registers a GET handler at this path that returns the current session state. The useAuthData composable calls this endpoint by default.
registerApiRoute
{ path: string }
Optional - When provided, the module registers a GET route backed by getApiListsController. Use this to expose the authenticated API token inventory to your frontend without manually wiring a route.
registerApiRoute.path
string
Required when registerApiRoute is set - The path where the module registers the API token inventory route. For example, "/api/auth/api-tokens".
When deploying to a serverless or edge environment such as Vercel, Netlify, or Cloudflare Workers, set enableFireWallBans: false in your plugin configuration. The firewall ban feature uses sudo ufw and is only available on Linux servers with UFW installed.
The bot detector in this module should be enabled by default, disabling it while using the IAM service will fail session rotation and verification.

Plugin configuration

The module does not configure the library automatically because the configuration includes non-serializable values such as storage instances and callbacks. You must create a Nitro plugin that calls defineAuthConfiguration.

defineAuthConfiguration initializes the library, starts the HTTP request logger, and registers all authentication routes on the Nitro router in one call. This is the recommended setup for Nuxt applications.

Using a config template

The module ships two config templates that read from environment variables, giving you a working baseline with no manual wiring:

TemplateDescription
configDefaultsCore authentication with HMAC signing and unstorage caching
configDefaultsWithOAuthExtends configDefaults with Google, GitHub, X, and LinkedIn OAuth providers

Create a Nitro plugin and spread the template as the base:

server/plugins/auth.ts
import { defineNitroPlugin } from 'nitropack/runtime'
import { useStorage } from 'nitropack/runtime/storage'
import { configDefaults } from 'auth-h3client/server/templates'
import { defineAuthConfiguration } from 'auth-h3client/v1'

export default defineNitroPlugin((nitroApp) => {
  defineAuthConfiguration(nitroApp, {
    ...configDefaults,
    onSuccessRedirect: '/dashboard',
    uStorage: {
      storage: useStorage('cache'),
      cacheOptions: {
        successTtl: 60 * 60 * 24 * 30,
        rateLimitTtl: 10
      }
    }
  })
})

configDefaults reads the following environment variables:

VariableRequiredDescription
AUTH_SERVER_LOCATIONYesHostname or IP address of the IAM service
AUTH_PORT_LOCATIONYesPort the IAM service listens on
HMAC_CLIENT_IDNoHMAC client identifier. Auto-generated if omitted
HMAC_SHARED_SECRETNoHMAC shared secret. Auto-generated if omitted
AUTH_CRYPTO_COOKIESNoSecret used to sign cookies. Auto-generated if omitted

When using configDefaultsWithOAuth, add the following variables:

VariableRequiredDescription
BASEURLYesFull base URL of your application, e.g. https://app.example.com
OAUTH_GOOGLE_CLIENT_IDYesGoogle OAuth client ID
OAUTH_GOOGLE_CLIENT_SECRETYesGoogle OAuth client secret
OAUTH_GITHUB_CLIENT_IDYesGitHub OAuth client ID
OAUTH_GITHUB_CLIENT_SECRETYesGitHub OAuth client secret
OAUTH_X_CLIENT_IDYesX (Twitter) OAuth client ID
OAUTH_X_CLIENT_SECRETYesX (Twitter) OAuth client secret
OAUTH_LINKEDIN_CLIENT_IDYesLinkedIn OAuth client ID
OAUTH_LINKEDIN_CLIENT_SECRETYesLinkedIn OAuth client secret
Production values for HMAC_CLIENT_ID, HMAC_SHARED_SECRET, and AUTH_CRYPTO_COOKIES should be stable across restarts and match what the IAM service expects. If you let the template auto-generate them, they will differ on every cold start.

Manual configuration

If you need full control over every option, pass the full configuration object directly to defineAuthConfiguration without a template. See the Configuration reference for a description of every field.

You can spread a template as the base and override only the fields you need, rather than writing the full object from scratch.
server/plugins/auth.ts
import { defineNitroPlugin } from 'nitropack/runtime'
import { useStorage } from 'nitropack/runtime/storage'
import { configDefaults } from 'auth-h3client/server/templates'
import { defineAuthConfiguration } from 'auth-h3client/v1'

export default defineNitroPlugin((nitroApp) => {
  defineAuthConfiguration(nitroApp, {
    ...configDefaults,
    // Override individual fields as needed
    onSuccessRedirect: '/dashboard',
    enableFireWallBans: false,
    logLevel: 'info',
    htmlSanitizer: {
      IrritationCount: 50,
      maxAllowedInputLength: 50000
    },
    imageUploader: {
      allowedBytes: 5_000_000,
      allowedMimes: ['image/png', 'image/jpeg', 'image/webp'],
      allowedExtensions: ['png', 'webp', 'jpeg', 'jpg']
    },
    uStorage: {
      storage: useStorage('cache'),
      cacheOptions: {
        successTtl: 60 * 60 * 24 * 30,
        rateLimitTtl: 10
      }
    }
  })
})

See the Configuration reference for a description of every field.


What the module registers

Global middleware

When enableMiddleware is true, the module registers a single server middleware that runs on every request before your route handlers. It skips HEAD requests, the /api/health path, Nuxt internal paths, and requests forwarded from localhost.

For all other requests it runs three operations in order:

  1. IP validation: extracts and validates the client IP and visitor fingerprint
  2. Bot detection: forwards the fingerprint to the IAM service check endpoint and returns 403 if the visitor is flagged
  3. CSRF cookie: mints a signed __Host-csrf cookie if one is not already present
Do not apply the Nuxt global auth middleware to routes that use defineAuthenticatePublicApi. Those routes handle machine-to-machine X-API-KEY verification, so running browser middleware on every request can trigger bot-detector rate limits or bans. Keep the middleware enabled for regular auth routes, because login, session rotation, MFA, getApiListsController, and defineApiManagementHandler still depend on bot detection and the CSRF cookie. In mixed apps, set enableMiddleware: false and register your own server middleware that skips the API-key routes.

Auth status route

The module registers a GET handler at authStatusUrl (default /api/auth/users/authStatus). This route proxies to the IAM service and returns the current session state. The useAuthData composable fetches this route during SSR and on the client.

Optional API token list route

When registerApiRoute is set, the module registers a GET handler at registerApiRoute.path using getApiListsController. This route returns the authenticated user's API token inventory and strips each token's public_identifier before the response reaches the browser.

Authentication routes

defineAuthConfiguration registers the following routes on the Nitro router:

MethodPathDescription
POST/loginEmail and password login
POST/signupUser registration
POST/logoutSession termination
GET/oauth/:providerInitiate an OAuth or OIDC flow
GET/POST/oauth/callback/:providerOAuth provider callback handler
GET/auth/bounceMagic link redirect entry point
GET/api/auth/verify-mfaMFA magic link validation
POST/api/auth/verify-mfaSubmit an MFA code
POST/api/auth/password-resetRequest a password reset link
GET/api/auth/reset-passwordPassword reset link validation
POST/api/auth/reset-passwordSubmit a new password
POST/api/auth/change-emailInitiate an email change
GET/api/auth/update-emailEmail change link validation
POST/api/auth/update-emailConfirm new email address

For the full middleware chain and request/response details for each route, see the Routes Reference.

Auto-imported server utilities

All server utilities from auth-h3client/v1 are auto-imported in your server/ directory. You do not need to import them manually inside any server route or plugin.

Auto-imported client composables

The following composables are auto-imported in your Vue components and pages:

ComposableDescription
useAuthDataReactive authentication state with SSR hydration
useMagicLinkReads and routes magic link query parameters
executeRequestFetch wrapper with CSRF injection and cookie forwarding
getCsrfTokenReads the CSRF token from the cookie

See Client-Side for full details.


Verify the setup

Start your development server and make a request to the auth status endpoint. With no active session the response should be:

Terminal
curl http://localhost:3000/api/auth/users/authStatus
{
  "authorized": false
}

A __Host-csrf cookie should also be present on the response. If the IAM service is unreachable, the auth status handler returns { "authorized": false } rather than erroring, so check the server logs if you expect a different result.

Create a protected endpoint

Try creating a simple endpoint and querying it in your frontend:

server/api/protected.get.ts
// This will only return successful response in the user its authenticate
export default defineAuthenticatedEventHandler(async (event) => { 
  const user = event.context.authorizedData; 
  return user;
})
app/components/component.vue
<script setup lang="ts">
import { executeRequest } from '@riavzon/auth-h3client/client';

const result = await executeRequest<Data>(
  '/api/protected',
  'GET',
  {},
  {},
  {},
  {
    headers: useRequestHeaders(),
    event: useRequestEvent(),
    fetcher: useRequestFetch(),
  },
)
const data = result.data
</script>

<template> 
  <div> {{ data }} </div>
</template>
Logo