Add authentication to your Nuxt 3 and Vue 3 applications (Logto)

Published: (December 22, 2025 at 06:45 AM EST)
5 min read
Source: Dev.to

Source: Dev.to

Before You Start: Create a Logto App in the Console

In Logto Console, create an application that matches your framework:

FrameworkLogto Application Type
Nuxt 3Traditional Web
Vue 3Single Page Application (SPA)

You’ll need the following values for both setups:

  • Endpoint – your tenant URL (e.g., https://your-tenant.logto.app)
  • App ID
  • App Secretonly for Nuxt

Part 1 — Nuxt 3 (SSR): Add Authentication with @logto/nuxt

Nuxt is often deployed with SSR (or hybrid rendering). Logto’s Nuxt SDK is designed for SSR and uses secure cookies + server‑side handling to complete authentication.

1️⃣ Install the Nuxt SDK

npm i @logto/nuxt
# or
pnpm add @logto/nuxt
# or
yarn add @logto/nuxt

2️⃣ Add Environment Variables

Create a .env file at the project root:

NUXT_LOGTO_ENDPOINT="https://your-tenant.logto.app"
NUXT_LOGTO_APP_ID="your-app-id"
NUXT_LOGTO_APP_SECRET="your-app-secret"
NUXT_LOGTO_COOKIE_ENCRYPTION_KEY="a-strong-random-string"

Important: NUXT_LOGTO_COOKIE_ENCRYPTION_KEY must be a strong random string because it protects encrypted auth cookies.

3️⃣ Configure nuxt.config.ts

// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@logto/nuxt'],

  runtimeConfig: {
    logto: {
      endpoint: process.env.NUXT_LOGTO_ENDPOINT,
      appId: process.env.NUXT_LOGTO_APP_ID,
      appSecret: process.env.NUXT_LOGTO_APP_SECRET,
      cookieEncryptionKey: process.env.NUXT_LOGTO_COOKIE_ENCRYPTION_KEY,
    },
  },
})

4️⃣ Configure Redirect URIs in Logto Console

Assuming your Nuxt app runs on http://localhost:3000:

Redirect TypeURI
Redirect URIhttp://localhost:3000/callback
Post sign‑out redirect URIhttp://localhost:3000/

These must match the callback and return URLs used by the SDK.

5️⃣ Understand the Built‑In Routes

The Nuxt module provides built‑in routes for the auth flow:

  • /sign-in
  • /sign-out
  • /callback

You can customize them:

// nuxt.config.ts
export default defineNuxtConfig({
  logto: {
    pathnames: {
      signIn: '/login',
      signOut: '/logout',
      callback: '/auth/callback',
    },
  },
})

If you change the callback path, update the redirect URI in Logto Console accordingly.

6️⃣ Implement a Simple Sign‑In / Sign‑Out Button

<script setup lang="ts">
const user = useLogtoUser()
</script>

<template>
  <button @click="user ? signOut() : signIn()">
    Sign {{ user ? 'out' : 'in' }}
  </button>

  <pre>{{ user }}</pre>
</template>

useLogtoUser() is reactive — it updates when the session changes.

7️⃣ Request Additional User Claims (Scopes)

By default you get basic OIDC claims. To request more (e.g., email, phone), add scopes:

// nuxt.config.ts
import { UserScope } from '@logto/nuxt'

export default defineNuxtConfig({
  logto: {
    scopes: [UserScope.Email, UserScope.Phone],
  },
})

8️⃣ Use the Logto Client (Server‑Only)

useLogtoClient() is server‑only; on the client it returns undefined. Example: fetch user info on the server once.

// composables/useServerUserInfo.ts
import { useLogtoClient, useState, callOnce } from '#imports'

export const useServerUserInfo = async () => {
  const client = useLogtoClient()
  const userInfo = useState('logto-user-info', () => null)

  await callOnce(async () => {
    if (!client) return
    if (!(await client.isAuthenticated())) return

    userInfo.value = await client.fetchUserInfo()
  })

  return userInfo
}

Part 2 — Vue 3 (SPA): Add Authentication with the Vue SDK

Vue SPAs don’t have a server runtime to securely store secrets, so the recommended setup uses the Logto Vue SDK and SPA application settings in Logto Console.

1️⃣ Install the Vue SDK

npm i @logto/vue
# or
pnpm add @logto/vue
# or
yarn add @logto/vue

2️⃣ Configure Redirect URIs in Logto Console

Assuming your Vue app runs on http://localhost:5173:

Redirect TypeURI
Redirect URIhttp://localhost:5173/callback
Post sign‑out redirect URIhttp://localhost:5173/

(Use your actual dev‑server origin if it’s different.)

3️⃣ Initialize Logto in main.ts

// src/main.ts
import { createApp } from 'vue'
import { createLogto } from '@logto/vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(
  createLogto({
    endpoint: import.meta.env.VITE_LOGTO_ENDPOINT,
    appId: import.meta.env.VITE_LOGTO_APP_ID,
  })
)

app.use(router)
app.mount('#app')

4️⃣ Add Environment Variables

Create a .env file (Vite will expose variables prefixed with VITE_):

VITE_LOGTO_ENDPOINT="https://your-tenant.logto.app"
VITE_LOGTO_APP_ID="your-app-id"

Add a callback route

In SPAs, you must handle the redirect callback route.

Example with Vue Router

// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../pages/Home.vue'
import Callback from '../pages/Callback.vue'

export default createRouter({
  history: createWebHistory(),
  routes: [
    { path: '/', component: Home },
    { path: '/callback', component: Callback },
  ],
})

Callback page

<script setup lang="ts">
import { onMounted } from 'vue'
import { useLogto } from '@logto/vue'
import { useRouter } from 'vue-router'

const { handleSignInCallback } = useLogto()
const router = useRouter()

onMounted(async () => {
  await handleSignInCallback(window.location.href)
  router.replace('/')
})
</script>

<template>
  <p>Signing you in…</p>
</template>

Sign‑in / Sign‑out buttons

<script setup lang="ts">
import { useLogto } from '@logto/vue'

const { isAuthenticated, signIn, signOut } = useLogto()

const handleSignIn = async () => {
  await signIn(import.meta.env.VITE_LOGTO_REDIRECT_URI)
}

const handleSignOut = async () => {
  await signOut(import.meta.env.VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI)
}
</script>

<template>
  <button @click="handleSignIn" v-if="!isAuthenticated">Sign in</button>
  <button @click="handleSignOut" v-else>Sign out</button>
</template>

Add redirect‑related env vars

VITE_LOGTO_REDIRECT_URI="http://localhost:5173/callback"
VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI="http://localhost:5173/"

Display the current user (optional)

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useLogto } from '@logto/vue'

const { isAuthenticated, getIdTokenClaims } = useLogto()
const claims = ref(null)

const loadClaims = async () => {
  claims.value = await getIdTokenClaims()
}
</script>

<template>
  <button @click="loadClaims" :disabled="!isAuthenticated">Load user claims</button>
  <pre v-if="claims">{{ claims }}</pre>
</template>

Choosing between Nuxt and Vue integrations

  • Nuxt (SSR or hybrid rendering): Use @logto/nuxt. It’s built for server‑side flows and secure cookie handling.
  • Vue SPA (no server runtime): Use @logto/vue and select the SPA application type in the Logto Console.

Quick sanity check

  • Clicking Sign in should redirect you to Logto’s hosted sign‑in screen.
  • After signing in, you should land on your callback route and be returned to the app.
  • Clicking Sign out should clear the shared session and redirect you to the post‑sign‑out URL.

Final notes

This document intentionally keeps the examples minimal and production‑oriented. Once authentication works, the next steps are typically:

  • Protecting routes (Nuxt middleware / Vue router guards)
  • Calling your APIs with access tokens
  • Adding organization/roles/permissions if your product needs them
Back to Blog

Related posts

Read more »