Nuxt Authentication

A guide to implementing authentication in Nuxt applications using the Nuxt Authentication module.

Install the module to your Nuxt application with one command:

npx nuxi module add nuxt-authentication

Then add the useNuxtAuthentication composable to your app.vue file to ensure that the isAuthenticated global state is populated on app load:

<script lang="ts" setup>
const { hasToken, isAuthenticated } = useNuxtAuthentication()
</script>

That's it! You can now use Nuxt Authentication in your Nuxt app ✨

Architecture

Nuxt Authentication uses the BFF (Backend for Frontend) pattern to handle authentication. The Nuxt server middleware acts as a proxy between the frontend and the backend, allowing for secure communication and token management.

This ensures that the tokens are not exposed to the frontend, and the authentication process is handled securely on the server side.

flowchart LR

A(Frontend) -->|Login Request| B(Nuxt Server Middleware)
B -->|Forward Request| C(Backend API)
C -->|Response with Tokens| B
B -->|Set Cookies| A

Composables

Checking for tokens

The useNuxtAuthentication composable can be used to check for the presence of authentication tokens:

<script lang="ts" setup>
const { hasToken, isAuthenticated } = useNuxtAuthentication()
</script>
The hasToken property indicates only if there is an access token present, while the cookie The isAuthenticated property indicates whether the user is authenticated based on the success or result of the authentication process. The presence of a token does not necessarily mean that the user is authenticated, as the token could be expired or invalid.

Verifying the access token

You can verify the authenticity of a user's access token with the verify function:

<script lang="ts" setup>
const { verify } = useNuxtAuthentication()
</script>

A failed verification will trigger the user-not-authenticated workflow based on the strategy set in the module options. The default strategy is renew, which will attempt to refresh the access token using the refresh token.

Refreshing a token

An access token can be refreshed using the useRefreshAccessTokencomposable:

<script lang="ts" setup>
const { renew } = useRefreshAccessToken()
</script>

Sending authenticated requests

To ensure that all of your requests are authenticated, you can use the $nuxtAuthentication helper:

const { $authenticatedFetch } = useNuxtApp()
const response = await $authenticatedFetch('/api/protected-endpoint', { method: 'GET' })
This helper automatically attaches the access token to the Authorization header of your requests and will also attempt to refresh the access token if it has expired if the refresh strategy is set to renew.

You can also use the useAuthenticatedFetch composable to create a custom fetch function that automatically attaches the access token to the Authorization header of your requests:

const { execute } = authenticatedFetch()
const response = await execute('/api/protected-endpoint', { method: 'GET' })

Login

<script lang="ts" setup>
const { usernameField, password, login } = useLogin('username')
const { userId, isAuthenticated } = useUser()
</script>

usernameField

The name of the field to be used to send the username (or email) to the backend.

throttle

Amount of time in milliseconds to throttle the login function (default: 1000).

redirectPath

The path to redirect the user to after a successful login (default: /).

Renderless component

You can also use the <nuxt-login> renderless component to create a custom login wrapper for your own UI:

<template>
  <section>
    <nuxt-container>
      <nuxt-card>
        <nuxt-input v-model="username" placeholder="Email" />
        <nuxt-input v-model="password" placeholder="Password" />

        <nuxt-login v-model:username-field="username" v-model:password-field="password">
          <template #default="{ login: login }">
            <nuxt-button @click="() => login()">
              Login via Slot
            </nuxt-button>
          </template>
        </nuxt-login>
      </nuxt-card>
    </nuxt-container>
  </section>
</template>

<script lang="ts" setup>
const username = ref('')
const password = ref('')
</script>

Logout

<template>
  <nuxt-button @click="useLogout">
    Logout
  </nuxt-button>
</template>

Checking user state

You can check for the user state with the useUser composable:

<script lang="ts" setup>
const { isAuthenticated, getUserId } = useUser()
</script>

userId (from getUserId())

The unique identifier of the authenticated user parsed from the JWT token.

isAuthenticated

A boolean indicating whether the user is authenticated or not.

Manually refreshing the access token

You can also manually refresh the access token with the useRefreshAccessToken composable:

<script lang="ts" setup>
const { renew } = useRefreshAccessToken()
await renew()
</script>

Module options

enabled

Enable or disable the module (default: true).

refreshEndpoint

The API endpoint to be used to refresh the access token (default: /api/token/refresh).

accessEndpoint

The API endpoint to be used to obtain a new access token (default: /api/token/access).

profileEndpoint

The API endpoint to be used to fetch the user profile (default: /api/auth/profile).

profileEndpointType

The type of the profile endpoint between api (e.g. restframework) and graphql (default: api).

profileEndpointFields

The fields to be fetched from the profile endpoint (applicable only for graphql type) (default: []).

profileGraphqlQuery

The GraphQL query to be used to fetch the user profile example user in query { user { id, email, username } } (applicable only for graphql type).

login:

The API endpoint to be used to log in (default: /login).

loginRedirectPath

The path to redirect the user to after a successful login (default: /).

strategy

The refresh strategy to be used when the access token expires (default: renew).

Possible values:

  • renew: Attempt to refresh the access token using the refresh token.
  • fail: Do not attempt to refresh the access token and consider the user as logged out.
  • redirect: Redirect the user to the login page.

bearerTokenType

The type of the bearer token to be used in the Authorization header (default: Token).

accessTokenName

The name of the access token cookie (default: 'access').

refreshTokenName

The name of the refresh token cookie (default: 'refresh').

accessTokenMaxAge

The maximum age of the access token in seconds (default: 300).

refreshTokenMaxAge

The maximum age of the refresh token in seconds (default: 1209600).

Authenticating with Rest Framework and Django

To authenticate with Django REST framework, you can use an TokenAuthSupportCookie that augments the TokenAuthentication class to support cookie based authentication. This allows you to authenticate users using the access token stored in a cookie.

from rest_framework.authentication import TokenAuthentication
from django.http import HttpRequest


class TokenAuthSupportCookie(TokenAuthentication):
    """Extends the TokenAuthentication class to support cookie based authentication"""

    access_token_name: str = 'access'

    def authenticate(self, request: HttpRequest):
        print(request.headers)
        if self.access_token_name in request.COOKIES and 'HTTP_AUTHORIZATION' not in request.META:
            token = request.COOKIES.get(self.access_token_name)
            return self.authenticate_credentials(token)
        return super().authenticate(request)

Contribution

Local development
  # Install dependencies
  pnpm install
  
  # Generate type stubs
  pnpm run dev:prepare
  
  # Develop with the playground
  pnpm run dev
  
  # Build the playground
  pnpm run dev:build
  
  # Run ESLint
  pnpm run lint
  
  # Run Vitest
  pnpm run test
  pnpm run test:watch
  pnpm run test:types
  pnpm run test:release

  # Release new version
  pnpm run release