DEV Community

Alex Kernel
Alex Kernel

Posted on

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

Logto provides official SDKs for multiple frameworks, but the integration approach depends on your app type:

  • SSR / full-stack frameworks (Nuxt, Next.js, etc.) typically need a server-aware SDK that can safely store secrets and manage redirect-based flows on the server.
  • SPA frameworks (Vue, React, etc.) typically use a client-side SDK and rely on redirect-based login without server secrets.

In this guide, you’ll implement Logto authentication twice:
1) Nuxt 3 (SSR) using @logto/nuxt

2) Vue 3 (SPA) using Logto’s Vue SDK

No fluff — just a production-ready, copy-paste-friendly walkthrough.


Before you start: create a Logto app in the Console

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

  • For Nuxt 3, create a Traditional Web application.
  • For Vue 3, create a Single Page Application (SPA) application.

You’ll need these values for both setups:


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
Enter fullscreen mode Exit fullscreen mode

2) Add environment variables

Create a .env file:

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"
Enter fullscreen mode Exit fullscreen mode

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,
    },
  },
})
Enter fullscreen mode Exit fullscreen mode

4) Configure redirect URIs in Logto Console

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

  • Redirect URI: http://localhost:3000/callback
  • Post sign-out redirect URI: http://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 if you want:

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

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

6) Implement a simple Sign in / Sign out button

<!-- pages/index.vue -->
<script setup lang="ts">
const user = useLogtoUser()
</script>

<template>
  <NuxtLink :to="`/sign-${user ? 'out' : 'in'}`">
    Sign {{ user ? 'out' : 'in' }}
  </NuxtLink>

  <pre v-if="user" style="margin-top: 16px">{{ user }}</pre>
</template>
Enter fullscreen mode Exit fullscreen mode

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

7) Request additional user claims (scopes)

By default, you get basic OIDC claims. If you need more (e.g., email/phone), add scopes:

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

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

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<any>('logto-user-info', () => null)

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

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

  return userInfo
}
Enter fullscreen mode Exit fullscreen mode

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

Install Logto’s Vue SDK (Vue 3 only):

npm i @logto/vue
# or
pnpm add @logto/vue
# or
yarn add @logto/vue
Enter fullscreen mode Exit fullscreen mode

2) Configure redirect URIs in Logto Console

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

  • Redirect URI: http://localhost:5173/callback
  • Post sign-out redirect URI: http://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')
Enter fullscreen mode Exit fullscreen mode

Add .env variables:

# .env
VITE_LOGTO_ENDPOINT="https://your-tenant.logto.app"
VITE_LOGTO_APP_ID="your-app-id"
Enter fullscreen mode Exit fullscreen mode

4) 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 },
  ],
})
Enter fullscreen mode Exit fullscreen mode

Callback page:

<!-- src/pages/Callback.vue -->
<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>
Enter fullscreen mode Exit fullscreen mode

5) Sign in / Sign out buttons

<!-- src/pages/Home.vue -->
<script setup lang="ts">
import { useLogto } from '@logto/vue'

const { isAuthenticated, signIn, signOut, getIdTokenClaims } = 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 v-if="!isAuthenticated" @click="handleSignIn">
    Sign in
  </button>

  <button v-else @click="handleSignOut">
    Sign out
  </button>
</template>
Enter fullscreen mode Exit fullscreen mode

Add redirect-related env vars:

VITE_LOGTO_REDIRECT_URI="http://localhost:5173/callback"
VITE_LOGTO_POST_SIGN_OUT_REDIRECT_URI="http://localhost:5173/"
Enter fullscreen mode Exit fullscreen mode

If you want to display the current user:

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

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

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

<template>
  <div v-if="isAuthenticated">
    <button @click="loadClaims">Load user claims</button>
    <pre v-if="claims">{{ claims }}</pre>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Choosing between Nuxt and Vue integrations

If you are building a Nuxt app with SSR (or hybrid rendering), prefer @logto/nuxt because it’s designed for server-side flows and secure cookie handling.

If you are building a Vue SPA (no server runtime), use @logto/vue and the SPA application type in 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 end up back in the app.
  • Clicking Sign out should clear the shared session and return you to your 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 it

Top comments (0)