npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@devcoffee/nuxt-core

v1.2.5

Published

Nuxt 4 module providing OpenID Connect / OAuth 2.0 authorization code grant with PKCE, server-side session management via Nitro, client-side auth state composables, and universal route protection middleware.

Downloads

659

Readme

@devcoffee/nuxt-core

npm version npm downloads License Nuxt

Full OpenID Connect / OAuth 2.0 authorization code grant with PKCE for DevCoffee internal Nuxt 4 applications. Provides server-side session management via Nitro, client-side auth state via Vue composables, and universal route protection middleware.

Features

  • PKCE-enforced authorization code flow (S256, enabled by default)
  • HMAC-SHA256 signed session cookies when sessions.secret is configured
  • AES-256-GCM encrypted tokenSet storage with HKDF-derived key
  • 256-bit session ID entropy via crypto.randomBytes(32)
  • Open redirect protection on all post-auth redirects
  • Per-route protection via definePageMeta — no page-level boilerplate
  • Auto-imported composables: useAuthContext, useSessionContext, useLogger
  • Server-side session validation on every Nitro request
  • Token refresh mutex — no concurrent refresh races
  • User info caching with configurable TTL
  • Nuxt DevTools integration (session inspector)

Requirements

  • Nuxt: ^4.0.0 — Nuxt 3 is not supported
  • Node.js: LTS (18+)
  • OIDC provider: Any provider with a .well-known/openid-configuration endpoint

Installation

npm install @devcoffee/nuxt-core

Register the module in nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['@devcoffee/nuxt-core'],
})

Quick Setup

1. Install and register the module (see Installation above).

2. Add the minimum config to nuxt.config.ts:

export default defineNuxtConfig({
  modules: ['@devcoffee/nuxt-core'],
  nuxtCore: {
    authts: {
      openid: {
        wellKnownUrl: process.env.OIDC_WELL_KNOWN_URL!,
        clientId: process.env.OIDC_CLIENT_ID!,
        clientSecret: process.env.OIDC_CLIENT_SECRET!,
        redirectUri: '/authorize',
        scopes: ['openid', 'profile', 'email'],
      },
      sessions: {
        secret: process.env.SESSION_SECRET!,
      },
    },
  },
})

3. Create the server handlerrequired. Auth endpoints return 404 without this file.

Create server/api/_auth/[...].ts:

export default NuxtAuthtsHandler({
  userInfo: async (user, { openidUser }) => ({
    ...user,
    id: openidUser?.sub ?? user.id,
    email: openidUser?.email ?? user.email,
    firstName: openidUser?.given_name ?? user.firstName,
    lastName: openidUser?.family_name ?? user.lastName,
  }),
})

The OIDC callback page at openid.redirectUri (default /authorize) is auto-registered by the module. Do NOT create it manually.

4. Set environment variables:

OIDC_WELL_KNOWN_URL=https://your-provider/.well-known/openid-configuration
OIDC_CLIENT_ID=your-client-id
OIDC_CLIENT_SECRET=your-client-secret
SESSION_SECRET=your-32-char-minimum-random-secret

Configuration Reference

All options are nested under nuxtCore in nuxt.config.ts.

authts.openid options

| Option | Type | Default | Description | | --- | --- | --- | --- | | wellKnownUrl | string | '' (required) | OIDC provider discovery URL (/.well-known/openid-configuration endpoint) | | clientId | string | '' (required) | OAuth 2.0 client ID registered with the OIDC provider | | clientSecret | string | '' (required) | OAuth 2.0 client secret | | redirectUri | string | '/authorize' | OIDC callback path. Must match the redirect URI registered with your provider. The module auto-registers this page. | | scopes | string[] | [] | OAuth scopes to request. Include 'openid' at minimum. | | usePkce | boolean | true | Enable PKCE (S256). Strongly recommended — disabling reduces security. | | codeChallengeMethod | string | 'S256' | PKCE code challenge method | | autoFetchUser | boolean | true | Fetch user info from the OIDC userinfo endpoint on GET_SESSION | | autoFetchUserTtl | number | 300 | Userinfo cache TTL in seconds. Prevents redundant OIDC provider calls. | | fetchUserOnLogin | boolean | true | Fetch user info immediately after the token exchange | | tokenRefreshBufferMs | number | 60000 | Refresh tokens this many ms before expiry | | cache.prefix | string | 'oidc-server-meta' | Nitro cache key prefix for OIDC server metadata | | cache.expires | number | 86400 | OIDC metadata cache TTL in seconds (24 hours) |

authts.sessions options

| Option | Type | Default | Description | | --- | --- | --- | --- | | secret | string | '' | Secret for HMAC-SHA256 cookie signing and AES-256-GCM tokenSet encryption. Must be set in production (see Security). Empty string disables signing and encryption. | | expiresIn | number | 518400 | Session lifetime in seconds (default 6 days) | | names.sessionId | string | 'auths.ssid' | Session ID cookie name | | names.state | string | 'auths.state' | OAuth state cookie name | | names.redirectUrl | string | 'auths.redirect' | Redirect URL cookie name | | names.pkce | string | 'auths.pkce' | PKCE verifier cookie name | | storage.name | string | 'sessions' | Nitro storage mount name | | storage.prefix | string | 'session' | Nitro storage key prefix | | cookieOpts.path | string | '/' | Cookie path | | cookieOpts.sameSite | string | 'lax' | Cookie SameSite attribute | | cookieOpts.httpOnly | boolean | true | Cookie HttpOnly flag | | cookieOpts.secure | boolean | true in production | Cookie Secure flag |

authts.auth options

| Option | Type | Default | Description | | --- | --- | --- | --- | | loginUri | string | '/login' | Path middleware redirects unauthenticated users to | | defaultLoginRedirectUri | string | '/' | Default post-login redirect when no intended destination is recorded | | defaultLogoutRedirectUri | string | '/login' | Post-logout redirect | | ignoreRegexPatterns | RegExp[] | [] | Routes matching these patterns are excluded from middleware in all environments | | ignoreRegexPatternsDev | RegExp[] | [] | Routes excluded from middleware in development only |

logging options

| Option | Type | Default | Description | | --- | --- | --- | --- | | logging.server.level | number | 2 | Server log level (0=silent, 1=fatal, 2=error, 3=warn, 4=info/debug) | | logging.server.tag | string | 'server' | Server log tag | | logging.ssr.tag | string | 'app-ssr' | SSR log tag | | logging.client.tag | string | 'app-client' | Client log tag |

Composables

All composables are auto-imported. No import statement needed.

useAuthContext(initiator?)

Reactive authentication state and actions.

const { login, logout, authorize, isAuthenticated, user, session, processing, sanitizeError } = useAuthContext()

// login — redirects to the OIDC provider's authorization endpoint
await login('/dashboard')

// logout — revokes tokens and redirects to defaultLogoutRedirectUri
await logout()

// authorize — exchanges the authorization code for tokens (called on the callback page)
await authorize(new URLSearchParams(window.location.search))

// isAuthenticated — ComputedRef<boolean>, true when the user has a valid session
console.log(isAuthenticated.value) // true | false

// user — ComputedRef<AuthorizedUser>, the current authenticated user
console.log(user.value.email)

// processing — Ref<boolean>, true while an auth request is in flight
console.log(processing.value) // true during login/logout/authorize

// sanitizeError — normalizes any error to H3Error
const h3err = sanitizeError(unknownError)

useSessionContext()

Low-level session accessor. Prefer useAuthContext for most use cases.

const { getValue, refetch } = useSessionContext()

// getValue — returns the current NuxtSessionContext
const session = getValue()

// refetch — triggers a session re-fetch from the server
await refetch()

useLogger(opts?)

Create a tagged logger instance.

const logger = useLogger({ tag: 'my-feature', level: 4 })

logger.debug('Debug message')
logger.warn('Warning message')
logger.error('Error message', error)

Log levels: 0 silent, 1 fatal, 2 error (default), 3 warn, 4 info/debug

Server Handlers

Server handlers are auto-imported into Nitro routes. No import needed.

NuxtAuthtsHandler(options?)

Main auth handler. Required — create this file or auth endpoints return 404.

// server/api/_auth/[...].ts
export default NuxtAuthtsHandler({
  userInfo: async (user, { openidUser }) => ({
    ...user,
    id: openidUser?.sub ?? user.id,
    email: openidUser?.email ?? user.email,
    firstName: openidUser?.given_name ?? user.firstName,
    lastName: openidUser?.family_name ?? user.lastName,
  }),
})

The userInfo callback maps OIDC claims to AuthorizedUser. It is called after the token exchange (when fetchUserOnLogin is true) and on every GET_SESSION request when autoFetchUser is true (results are cached for autoFetchUserTtl seconds).

NuxtForwardRequestHandler(opts)

Authenticated reverse proxy — forwards requests to a backend service with the session access token attached.

// server/api/backend/[...].ts
export default NuxtForwardRequestHandler({
  targetBaseUrl: process.env.BACKEND_API_URL,
  proxyPrefix: '/api/backend',
})

Route Protection

The module registers a global Nuxt middleware that runs on every navigation. Control access per page with definePageMeta.

Require authentication

<!-- pages/dashboard.vue -->
<script setup lang="ts">
definePageMeta({
  authts: { required: true },
})

const { user, isAuthenticated, logout } = useAuthContext()
</script>

<template>
  <div>
    <p>Welcome, {{ user.firstName }}</p>
    <button @click="logout()">Sign out</button>
  </div>
</template>

Unauthenticated users are redirected to auth.loginUri (default /login). The intended URL is preserved as the post-login redirect.

Restrict to unauthenticated users (login page)

<!-- pages/login.vue -->
<script setup lang="ts">
definePageMeta({
  authts: { unauthenticatedOnly: true },
})

const { login } = useAuthContext()
</script>

<template>
  <button @click="login('/dashboard')">Sign in</button>
</template>

Authenticated users navigating to this page receive a 404 from middleware.

Warning: Do not set both required: true and unauthenticatedOnly: true on the same page. This combination causes a middleware error.

Note: roles is a reserved field in AuthtsMiddlewareMeta and is not currently enforced.

Nuxt Hooks

// plugins/my-app.ts
export default defineNuxtPlugin((_nuxtApp) => {
  // Fires after every successful authorization code exchange
  _nuxtApp.hook('user:loggedIn', async () => {
    console.log('User logged in — session is now populated')
  })

  // Fires after successful logout
  _nuxtApp.hook('user:loggedOut', async () => {
    console.log('User logged out — session has been cleared')
  })

  // Trigger a session re-fetch manually (e.g. after a server-side state change)
  // _nuxtApp.callHook('session:fetch', 'my-initiator')

  // Fires after every session fetch — receives the latest session data
  _nuxtApp.hook('session:changed', async (session) => {
    console.log('Session updated:', session.isAuthenticated)
  })
})

TypeScript

import type {
  AuthorizedUser,
  AuthtsMiddlewareMeta,
  NuxtSessionContext,
  SessionContext,
} from '@devcoffee/nuxt-core'

Custom session data

SessionContext and NuxtSessionContext accept a generic TData parameter for type-safe custom session data.

import type { SessionContext } from '@devcoffee/nuxt-core'

type MyAppData = {
  preferences: { theme: string }
  roles: string[]
}

// In a server handler or Nitro plugin:
const session: SessionContext<MyAppData> = await getSession(sessionId, opts)

// Fully typed — no casting required
console.log(session.data.preferences.theme) // string
console.log(session.data.roles)             // string[]

Security

Production checklist

1. Set sessions.secret — most important step.

Without it, session ID cookies are unsigned and tokenSet storage is unencrypted. An attacker who gains read access to Nitro storage can extract access tokens.

Set a 32+ character random string:

SESSION_SECRET=your-32-char-minimum-random-secret

When sessions.secret is configured:

  • HMAC-SHA256 signs the session ID cookie — tampering is detected with constant-time comparison
  • AES-256-GCM encrypts the tokenSet in Nitro storage — keys are derived via HKDF-SHA256 with domain separation

2. PKCE is enabled by defaultusePkce: true with S256. Do not disable it.

3. Open redirect protectionisSameOrigin() validates all post-auth redirects. External URLs are rejected with HTTP 400.

4. Session ID entropycrypto.randomBytes(32), 256-bit. UUID v4 is not used.

5. HttpOnly cookieshttpOnly: true and secure: true in production by default.

Changelog

See CHANGELOG.md for the full release history.

Contributing

See GUIDELINE.md for contribution guidelines, local development setup, and release instructions.