Add authentication to your Nuxt 3 and Vue 3 applications (Logto)
Source: Dev.to
Before You Start: Create a Logto App in the Console
In Logto Console, create an application that matches your framework:
| Framework | Logto Application Type |
|---|---|
| Nuxt 3 | Traditional Web |
| Vue 3 | Single 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 Secret – only 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_KEYmust 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 Type | URI |
|---|---|
| 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:
// 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 Type | URI |
|---|---|
| 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')
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/vueand 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