nuxt-auth-utils

repository·main·Indexed 23 days ago

https://github.com/atinux/nuxt-auth-utils

A Nuxt module for adding authentication using secured and sealed cookie sessions. It supports hybrid rendering, over 40 OAuth providers, password hashing via scrypt, and WebAuthn (passkeys). Features include the useUserSession() composable for client-side state, server-side session management helpers, and the <AuthState> component for safe UI rendering.

Tokens
15.2K
Snippets
30
Records
61
Agent score
81%

What's inside nuxt-auth-utils

  1. Implement authenticated WebSocket connections

    main

    Nuxt Auth Utils is compatible with Nitro WebSockets. To protect WebSocket connections, use requireUserSession(request) within the upgrade handler to verify authentication before allowing the connection to upgrade. You can then use requireUserSession(peer) within the open handler to access the user session.

    // server/routes/ws.ts
    export default defineWebSocketHandler({
      async upgrade(request) {
        // Make sure the user is authenticated before upgrading the WebSocket connection
        await requireUserSession(request)
      },
      async open(peer) {
        const { user } = await requireUserSession(peer)
    
        peer.send(`Hello, ${user.name}!`)
      },
      message(peer, message) {
        peer.send(`Echo: ${message}`)
      },
    })
  2. Install nuxt-auth-utils

    main

    To add authentication to your Nuxt project, use the nuxi module add command.

    Note: This module requires a Nuxt server running (it uses server API routes) and is not compatible with nuxt generate. However, you can still use Hybrid Rendering to pre-render pages or disable SSR.

    npx nuxi@latest module add auth-utils
  3. Make authenticated requests during SSR

    main

    When performing Server-Side Rendering (SSR), you must ensure authenticated requests include credentials. If you are using useAsyncData, use useRequestFetch() instead of a standard $fetch. If you are using useFetch, it will automatically use useRequestFetch during SSR.

    <script setup lang="ts">
    // When using useAsyncData
    const { data } = await useAsyncData('team', () => useRequestFetch()('/api/protected-endpoint'))
    
    // useFetch will automatically use useRequestFetch during SSR
    const { data } = await useFetch('/api/protected-endpoint')
    </script>
  4. Enable AT Protocol (Bluesky) support

    main

    To use OAuth with the AT Protocol (e.g., Bluesky), follow these steps:

    1. Install the required peer dependencies:
      npx nypm i @atproto/oauth-client-node @atproto/api
    2. Enable it in your nuxt.config.ts:
      export default defineNuxtConfig({
        auth: {
          atproto: true
        }
      })
    // nuxt.config.ts
    export default defineNuxtConfig({
      auth: {
        atproto: true
      }
    })
  5. Implement OAuth authentication handlers

    main

    Use defineOAuth<Provider>EventHandler to create server routes that handle OAuth flows. The helper automatically redirects users to the provider's authorization page and executes onSuccess or onError callbacks.

    Configuration: Providers can be configured in nuxt.config.ts under runtimeConfig.oauth.<provider> or via environment variables:

    • NUXT_OAUTH_<PROVIDER>_CLIENT_ID (e.g., NUXT_OAUTH_GITHUB_CLIENT_ID)
    • NUXT_OAUTH_<PROVIDER>_CLIENT_SECRET
    • NUXT_OAUTH_<PROVIDER>_REDIRECT_URL (to override the default redirect URL)

    Callback URL Pattern: Ensure your OAuth provider settings use the callback URL: <your-domain>/auth/<provider> (e.g., /auth/github).

    // ~/server/routes/auth/github.get.ts
    export default defineOAuthGitHubEventHandler({
      config: {
        emailRequired: true
      },
      async onSuccess(event, { user, tokens }) {
        await setUserSession(event, {
          user: {
            githubId: user.id
          }
        })
        return sendRedirect(event, '/')
      },
      onError(event, error) {
        console.error('GitHub OAuth error:', error)
        return sendRedirect(event, '/')
      },
    })
  6. Implement WebAuthn (Passkeys)

    main

    WebAuthn allows users to authenticate using biometrics or physical security keys.

    Setup:

    1. Install peer dependencies:
      npx nypm i @simplewebauthn/server@11 @simplewebauthn/browser@11
    2. Enable in nuxt.config.ts:
      export default defineNuxtConfig({
        auth: {
          webAuthn: true
        }
      })

    Server Implementation: Use defineWebAuthnRegisterEventHandler for registration and defineWebAuthnAuthenticateEventHandler for login.

    Security Note: It is highly recommended to use challenges to prevent replay attacks. Implement storeChallenge and getChallenge in your handler to manage challenges using useStorage() or a database.

    <script setup lang="ts">
    const { register, authenticate } = useWebAuthn({
      registerEndpoint: '/api/webauthn/register',
      authenticateEndpoint: '/api/webauthn/authenticate',
    })
    const { fetch: fetchUserSession } = useUserSession()
    
    const userName = ref('')
    async function signUp() {
      await register({ userName: userName.value }).then(fetchUserSession)
    }
    
    async function signIn() {
      await authenticate(userName.value).then(fetchUserSession)
    }
    </script>
  7. Configure session load strategies for Hybrid Rendering

    main

    In hybrid rendering scenarios (like prerendering or caching via routeRules), the user session cannot be accessed during prerendering because it is stored in a secure cookie. You can control how the session is loaded using the loadStrategy option in nuxt.config.ts:

    • client-only: The session is fetched only on the client-side after hydration. You can still manually fetch it on the server using useUserSession().fetch().
    • none: Disables automatic session loading. You must manually fetch the session using useUserSession().fetch().

    Note: If caching routes with routeRules, ensure you are using Nitro >= 2.9.7 to support client-side session fetching.

  8. Configure NUXT_SESSION_PASSWORD

    main

    Nuxt Auth Utils requires a NUXT_SESSION_PASSWORD environment variable in your .env file to secure and seal cookie sessions. The password must be at least 32 characters long.

    If you are running Nuxt in development and haven't set this variable, Nuxt Auth Utils will generate one for you automatically.

    # .env
    NUXT_SESSION_PASSWORD=password-with-at-least-32-characters
  9. Configure session defaults

    main

    You can configure session behavior by overriding runtimeConfig.session in your nuxt.config.ts. This leverages h3 useSession options.

    Default values:

    • name: 'nuxt-session'
    • password: process.env.NUXT_SESSION_PASSWORD
    • cookie.sameSite: 'lax'
    export default defineNuxtConfig({
      modules: ['nuxt-auth-utils'],
      runtimeConfig: {
        session: {
          maxAge: 60 * 60 * 24 * 7 // 1 week
        }
      }
    })
  10. Extend session data with sessionHooks

    main

    You can use sessionHooks within a Nitro plugin to extend session data during fetch or perform actions when a session is cleared.

    • sessionHooks.hook('fetch', ...): Triggered when a session is fetched (e.g., during SSR or via useUserSession().fetch()). Use this to augment the session with database data.
    • sessionHooks.hook('clear', ...): Triggered when a session is cleared (e.g., via clearUserSession(event) or useUserSession().clear()).
    // server/plugins/session.ts
    export default defineNitroPlugin(() => {
      sessionHooks.hook('fetch', async (session, event) => {
        // Extend User Session by calling your database
      })
    
      sessionHooks.hook('clear', async (session, event) => {
        // Log that user logged out
      })
    })
  11. Manage user sessions with Server Utils

    main

    Nuxt Auth Utils provides auto-imported helpers in your server/ directory to manage encrypted user sessions.

    Key behaviors:

    • setUserSession(event, data): Sets a session. It merges new data with existing data using unjs/defu.
    • replaceUserSession(event, data): Replaces the entire session without merging.
    • getUserSession(event): Retrieves the current session.
    • clearUserSession(event): Removes the current session.
    • requireUserSession(event): Retrieves the session or returns a 401 error if the user key is missing.

    Session Structure:

    • user: Public data used to recognize the user.
    • secure: Private data accessible only on server-side routes.
    • Custom fields can be added to the session object.

    Important: Session data is stored in cookies and is limited to a 4096-byte size limit. Only store essential information.

    // Set a user session (merges data)
    await setUserSession(event, {
      user: { login: 'atinux' },
      secure: { apiToken: '1234567890' },
      loggedInAt: new Date()
    })
    
    // Replace a user session (does not merge)
    await replaceUserSession(event, data)
    
    // Get the current user session
    const session = await getUserSession(event)
    
    // Clear the current user session
    await clearUserSession(event)
    
    // Require a user session (throws 401 if no `user` key)
    const session = await requireUserSession(event)
  12. Set session options during user session creation

    main

    When calling setUserSession or replaceUserSession on the server, you can pass a third argument to override session configuration (like maxAge) for that specific session.

    await setUserSession(event, { ... } , {
      maxAge: 60 * 60 * 24 * 7 // 1 week
    })