## Documentation Index

Fetch the complete documentation index at: [/llms.txt](/content/llms.txt)

Use this file to discover all available pages before exploring further.

Use AI to integrate Auth0

If you use an AI coding assistant like Claude Code, Cursor, or GitHub Copilot, you can add Auth0 authentication automatically in minutes using [agent skills](https://agentskills.io/home).**Install:**

```
npx skills add auth0/agent-skills --skill auth0
```

**Then ask your AI assistant:**

```
Add Auth0 authentication to my Vue app
```

Your AI assistant will automatically create your Auth0 application, fetch credentials, install `@auth0/auth0-vue`, create the authentication plugin, and configure your routes. [Full agent skills documentation →](/content/docs/quickstart/agent-skills/index.html)

**Prerequisites:** Before you begin, ensure you have the following installed:

- **[Node.js](https://nodejs.org/en/download)** 20 LTS or newer
- **[npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm)** 10+ or **[yarn](https://classic.yarnpkg.com/lang/en/docs/install/)** 1.22+ or **[pnpm](https://pnpm.io/installation)** 8+

Verify installation: `node --version && npm --version`**Vue Version Compatibility:** This quickstart works with **Vue 3.x** and newer.

## Get Started

This quickstart demonstrates how to integrate Auth0 authentication into a Vue.js 3 application. You’ll build a responsive single-page app with secure user authentication using Vue’s composition API and the Auth0 Vue SDK.

1. **Create a new project**

Create a new Vue 3 project for this Quickstart
   
   ```
   npm create vue@latest auth0-vue -- --typescript --router --pinia
   ```
   
   Open the project
   
   ```
   cd auth0-vue
   ```

2. **Install the Auth0 Vue SDK**

```
   npm add @auth0/auth0-vue && npm install
   ```

3. **Setup your Auth0 App**

Create a new app on your Auth0 tenant and add the environment variables to your project. You have three options to set up your Auth0 app: use the Quick Setup tool (recommended), run a CLI command, or configure manually via the Dashboard:

- Quick Setup (recommended)
   - CLI
   - Dashboard

Create an Auth0 App and copy the pre-filled `.env` file with the right configuration values.

Log into create the app
   
   .env
   
   ```
   VITE_AUTH0_DOMAIN={yourDomain}
   VITE_AUTH0_CLIENT_ID={yourClientId}
   ```

Run the following command in your project’s root directory to create an Auth0 app and generate a `.env` file:

```
   # Install Auth0 CLI (if not already installed)
   brew tap auth0/auth0-cli && brew install auth0
   # Set up Auth0 app and generate .env file
   auth0 qs setup --app --type spa --framework vue --build-tool vite --name "My App" --port 5173
   ```
   
   ```
   # Install Auth0 CLI (if not already installed)
   scoop bucket add auth0 https://github.com/auth0/scoop-auth0-cli.git
   scoop install auth0
   # Set up Auth0 app and generate .env file
   auth0 qs setup --app --type spa --framework vue --build-tool vite --name "My App" --port 5173
   ```

This command will:
   1. Check if you’re authenticated (and prompt for login if needed)
   2. Create an Auth0 Single Page Application configured for `http://localhost:5173`
   3. Generate a `.env` file with `VITE_AUTH0_DOMAIN` and `VITE_AUTH0_CLIENT_ID`

Before you start, create a `.env` file on your project’s root directory
   
   .env
   
   ```
   VITE_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
   VITE_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
   ```
   
   1. Head to the [Auth0 Dashboard](https://manage.auth0.com/dashboard/)
   2. Click on **Applications** > **Applications** > **Create Application**
   3. In the popup, enter a name for your app, select `Single Page Web Application` as the app type and click **Create**
   4. Switch to the **Settings** tab on the Application Details page
   5. Replace `YOUR_AUTH0_APP_DOMAIN` and `YOUR_AUTH0_APP_CLIENT_ID` on the `.env` file with the **Domain** and **Client ID** values from the dashboard

Finally, on the **Settings** tab of your Application Details page, configure the following URLs:
   
   **Allowed Callback URLs:**
   
   ```
   http://localhost:5173
   ```

**Allowed Logout URLs:**
   
   ```
   http://localhost:5173
   ```

**Allowed Web Origins:**
   
   ```
   http://localhost:5173
   ```

**Allowed Callback URLs** are a critical security measure to ensure users are safely returned to your application after authentication. Without a matching URL, the login process will fail, and users will be blocked by an Auth0 error page instead of accessing your app.

**Allowed Logout URLs** are essential for providing a seamless user experience upon signing out. Without a matching URL, users will not be redirected back to your application after logout and will instead be left on a generic Auth0 page.

**Allowed Web Origins** is critical for silent authentication. Without it users will be logged out when they refresh the page or return to your app later.

Verify your `.env` file exists: `cat .env` (Mac/Linux) or `type .env` (Windows)

4. **Configure the Auth0 Plugin**

src/main.ts
   
   ```
   import './assets/main.css'

import { createApp } from 'vue'
   import { createPinia } from 'pinia'
   import router from './router'
   import { createAuth0 } from '@auth0/auth0-vue'

import App from './App.vue'

const app = createApp(App)

app.use(createAuth0({
     domain: import.meta.env.VITE_AUTH0_DOMAIN,
     clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
     authorizationParams: {
       redirect_uri: window.location.origin
     }
   }))

app.use(createPinia())
   app.use(router)

app.mount('#app')
   ```

5. **Create Authentication Components**

Create component files
   
   Mac/Linux
   
   ```
   touch src/components/LoginButton.vue && touch src/components/LogoutButton.vue && touch src/components/UserProfile.vue
   ```
   
   Windows
   
   ```
   New-Item -ItemType File -Path src/components/LoginButton.vue
   New-Item -ItemType File -Path src/components/LogoutButton.vue
   New-Item -ItemType File -Path src/components/UserProfile.vue
   ```

And add the following code snippets
   
   src/components/LoginButton.vue
   
   ```
   <template>
     <button
       @click="handleLogin"
       class="button login"
       :disabled="isLoading"
     >
       {{ isLoading ? 'Loading...' : 'Log In' }}
     </button>
   </template>

<script setup lang="ts">
   import { useAuth0 } from '@auth0/auth0-vue'

const { loginWithRedirect, isLoading } = useAuth0()

const handleLogin = () => {
     loginWithRedirect()
   }
   </script>
   ```
   
   src/components/LogoutButton.vue
   
   ```
   <template>
     <button
       @click="handleLogout"
       class="button logout"
       :disabled="isLoading"
     >
       {{ isLoading ? 'Loading...' : 'Log Out' }}
     </button>
   </template>
   
   <script setup lang="ts">
   import { useAuth0 } from '@auth0/auth0-vue'

const { logout, isLoading } = useAuth0()

const handleLogout = () => {
     logout({
       logoutParams: {
         returnTo: window.location.origin
       }
     })
   }
   </script>
   ```
   
   src/components/UserProfile.vue
   
   ```
   <template>
     <div v-if="isLoading" class="loading-text">
       Loading profile...
     </div>
     <div
       v-else-if="isAuthenticated && user"
       class="profile-container"
     >
       <img
         :src="user.picture || placeholderImage"
         :alt="user.name || 'User'"
         class="profile-picture"
         @error="handleImageError"
       />
       <div class="profile-info">
         <div class="profile-name">
           {{ user.name }}
         </div>
         <div class="profile-email">
           {{ user.email }}
         </div>
       </div>
     </div>
   </template>

<script setup lang="ts">
   import { useAuth0 } from '@auth0/auth0-vue'

const { user, isAuthenticated, isLoading } = useAuth0()

const placeholderImage = `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='50' fill='%2363b3ed'/%3E%3Cpath d='M50 45c7.5 0 13.64-6.14 13.64-13.64S57.5 17.72 50 17.72s-13.64 6.14-13.64 13.64S42.5 45 50 45zm0 6.82c-9.09 0-27.28 4.56-27.28 13.64v3.41c0 1.88 1.53 3.41 3.41 3.41h47.74c1.88 0 3.41-1.53 3.41-3.41v-3.41c0-9.08-18.19-13.64-27.28-13.64z' fill='%23fff'/%3E%3C/svg%3E`

function handleImageError(e: Event) {
     const target = e.target as HTMLImageElement
     target.src = placeholderImage
   }
   </script>
   
   <style scoped>
   .profile-container {
     display: flex;
     flex-direction: column;
     align-items: center;
     gap: 1rem;
   }

.profile-picture {
     width: 110px;
     height: 110px;
     border-radius: 50%;
     object-fit: cover;
     border: 3px solid #63b3ed;
     transition: transform 0.3s ease-in-out;
   }

.profile-picture:hover {
     transform: scale(1.05);
   }

.profile-info {
     text-align: center;
   }

.profile-name {
     font-size: 2rem;
     font-weight: 600;
     color: #f7fafc;
     margin-bottom: 0.5rem;
   }

.profile-email {
     font-size: 1.15rem;
     color: #a0aec0;
   }
   </style>
   ```
   
   src/App.vue
   
   ```
   <template>
     <div class="app-container">
       <div v-if="isLoading" class="loading-state">
         <div class="loading-text">Loading...</div>
       </div>

<div v-else-if="error" class="error-state">
         <div class="error-title">Oops!</div>
         <div class="error-message">Something went wrong</div>
         <div class="error-sub-message">{{ error.message }}</div>
       </div>

<div v-else class="main-card-wrapper">
         <img
           src="https://cdn.auth0.com/quantum-assets/dist/latest/logos/auth0/auth0-lockup-en-ondark.png"
           alt="Auth0 Logo"
           class="auth0-logo"
           @error="handleImageError"
         />
         <h1 class="main-title">Welcome to Vue0</h1>

<div v-if="isAuthenticated" class="logged-in-section">
           <div class="logged-in-message">✅ Successfully authenticated!</div>
           <h2 class="profile-section-title">Your Profile</h2>
           <div class="profile-card">
             <UserProfile />
           </div>
           <LogoutButton />
         </div>

<div v-else class="action-card">
           <p class="action-text">Get started by signing in to your account</p>
           <LoginButton />
         </div>
       </div>
     </div>
   </template>

<script setup lang="ts">
   import { useAuth0 } from '@auth0/auth0-vue'
   import LoginButton from './components/LoginButton.vue'
   import LogoutButton from './components/LogoutButton.vue'
   import UserProfile from './components/UserProfile.vue'

const { isAuthenticated, isLoading, error } = useAuth0()

const handleImageError = (e: Event) => {
     const target = e.target as HTMLImageElement
     target.style.display = 'none'
   }
   </script>

<style>
   @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');

* {
     margin: 0;
     padding: 0;
     box-sizing: border-box;
   }

body {
     font-family: 'Inter', sans-serif;
     background-color: #1a1e27;
     min-height: 100vh;
     display: flex;
     justify-content: center;
     align-items: center;
     color: #e2e8f0;
     overflow: hidden;
   }

#app {
     width: 100%;
     height: 100%;
     display: flex;
     justify-content: center;
     align-items: center;
   }

.app-container {
     display: flex;
     flex-direction: column;
     justify-content: center;
     align-items: center;
     min-height: 100vh;
     width: 100%;
     padding: 1rem;
   }

.loading-state, .error-state {
     background-color: #2d313c;
     border-radius: 15px;
     box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4);
     padding: 3rem;
     text-align: center;
   }

.loading-text {
     font-size: 1.8rem;
     font-weight: 500;
     color: #a0aec0;
     animation: pulse 1.5s infinite ease-in-out;
   }

.error-state {
     background-color: #c53030;
     color: #fff;
   }

.error-title {
     font-size: 2.8rem;
     font-weight: 700;
     margin-bottom: 0.5rem;
   }

.error-message {
     font-size: 1.3rem;
     margin-bottom: 0.5rem;
   }

.error-sub-message {
     font-size: 1rem;
     opacity: 0.8;
   }

.main-card-wrapper {
     background-color: #262a33;
     border-radius: 20px;
     box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);
     display: flex;
     flex-direction: column;
     align-items: center;
     gap: 2rem;
     padding: 3rem;
     max-width: 500px;
     width: 90%;
     animation: fadeInScale 0.8s ease-out forwards;
   }

.auth0-logo {
     width: 160px;
     margin-bottom: 1.5rem;
     opacity: 0;
     animation: slideInDown 1s ease-out forwards 0.2s;
   }

.main-title {
     font-size: 2.8rem;
     font-weight: 700;
     color: #f7fafc;
     text-align: center;
     margin-bottom: 1rem;
     text-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);
     opacity: 0;
     animation: fadeIn 1s ease-out forwards 0.4s;
   }

.action-card {
     background-color: #2d313c;
     border-radius: 15px;
     box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.3), 0 5px 15px rgba(0, 0, 0, 0.3);
     padding: 2.5rem;
     display: flex;
     flex-direction: column;
     align-items: center;
     gap: 1.8rem;
     width: calc(100% - 2rem);
     opacity: 0;
     animation: fadeIn 1s ease-out forwards 0.6s;
   }

.action-text {
     font-size: 1.25rem;
     color: #cbd5e0;
     text-align: center;
     line-height: 1.6;
     font-weight: 400;
   }

.button {
     padding: 1.1rem 2.8rem;
     font-size: 1.2rem;
     font-weight: 600;
     border-radius: 10px;
     border: none;
     cursor: pointer;
     transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
     box-shadow: 0 8px 20px rgba(0, 0, 0, 0.4);
     text-transform: uppercase;
     letter-spacing: 0.08em;
     outline: none;
   }

.button:focus {
     box-shadow: 0 0 0 4px rgba(99, 179, 237, 0.5);
   }

.button:disabled {
     opacity: 0.6;
     cursor: not-allowed;
     transform: none;
   }

.button.login {
     background-color: #63b3ed;
     color: #1a1e27;
   }

.button.login:hover:not(:disabled) {
     background-color: #4299e1;
     transform: translateY(-5px) scale(1.03);
     box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
   }

.button.logout {
     background-color: #fc8181;
     color: #1a1e27;
   }

.button.logout:hover:not(:disabled) {
     background-color: #e53e3e;
     transform: translateY(-5px) scale(1.03);
     box-shadow: 0 12px 25px rgba(0, 0, 0, 0.5);
   }

.logged-in-section {
     display: flex;
     flex-direction: column;
     align-items: center;
     gap: 1.5rem;
     width: 100%;
   }

.logged-in-message {
     font-size: 1.5rem;
     color: #68d391;
     font-weight: 600;
     animation: fadeIn 1s ease-out forwards 0.8s;
   }

.profile-section-title {
     font-size: 2.2rem;
     animation: slideInUp 1s ease-out forwards 1s;
   }

.profile-card {
     padding: 2.2rem;
     animation: scaleIn 0.8s ease-out forwards 1.2s;
   }

@keyframes fadeIn {
     from { opacity: 0; }
     to { opacity: 1; }
   }

@keyframes fadeInScale {
     from { opacity: 0; transform: scale(0.95); }
     to { opacity: 1; transform: scale(1); }
   }

@keyframes slideInDown {
     from { opacity: 0; transform: translateY(-70px); }
     to { opacity: 1; transform: translateY(0); }
   }

@keyframes slideInUp {
     from { opacity: 0; transform: translateY(50px); }
     to { opacity: 1; transform: translateY(0); }
   }

@keyframes pulse {
     0%, 100% { opacity: 1; }
     50% { opacity: 0.6; }
   }

@keyframes scaleIn {
     from { opacity: 0; transform: scale(0.8); }
     to { opacity: 1; transform: scale(1); }
   }

@media (max-width: 600px) {
     .main-card-wrapper {
       padding: 2rem;
       margin: 1rem;
     }

.main-title {
       font-size: 2.2rem;
     }

.button {
       padding: 1rem 2rem;
       font-size: 1.1rem;
     }

.auth0-logo {
       width: 120px;
     }
   }
   </style>
   ```

6. **Run your app**

```
   npm run dev
   ```

If port 5173 is in use, run: `npm run dev -- --port 5174` and update your Auth0 app’s callback URLs to `http://localhost:5174`

**Checkpoint**
You should now have a fully functional Auth0 login page running on your [localhost](http://localhost:5173/)

---

## Advanced Usage

**Protecting Routes with Vue Router**

Use Vue Router’s navigation guards to protect specific routes:

src/router/index.ts

```javascript
import { createRouter, createWebHistory } from 'vue-router'
import { authGuard } from '@auth0/auth0-vue'
import Home from '../views/Home.vue'
import Profile from '../views/Profile.vue'
import Dashboard from '../views/Dashboard.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/profile',
    name: 'Profile',
    component: Profile,
    beforeEnter: authGuard
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: Dashboard,
    beforeEnter: authGuard
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router
```

**Calling Protected APIs**

Configure your Auth0 plugin to include an API audience and make authenticated requests:

src/main.ts

```javascript
import { createAuth0 } from '@auth0/auth0-vue'

const app = createApp(App)

app.use(
  createAuth0({
    domain: import.meta.env.VITE_AUTH0_DOMAIN,
    clientId: import.meta.env.VITE_AUTH0_CLIENT_ID,
    authorizationParams: {
      redirect_uri: window.location.origin,
      audience: "YOUR_API_IDENTIFIER"
    }
  })
)
```

Then make authenticated API calls in your components:

src/components/ApiCall.vue

```vue
<template>
  <div>
    <button @click="callProtectedApi" :disabled="isLoading">
      Call Protected API
    </button>
    <pre v-if="apiResponse">{{ JSON.stringify(apiResponse, null, 2) }}</pre>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { useAuth0 } from '@auth0/auth0-vue'

const { getAccessTokenSilently } = useAuth0()
const apiResponse = ref(null)
const isLoading = ref(false)

const callProtectedApi = async () => {
  try {
    isLoading.value = true
    const token = await getAccessTokenSilently()

const response = await fetch('/api/protected', {
      headers: {
        Authorization: `Bearer ${token}`
      }
    })

apiResponse.value = await response.json()
  } catch (error) {
    console.error('API call failed:', error)
  } finally {
    isLoading.value = false
  }
}
</script>
```

**Using Composables for Authentication Logic**

Create reusable composables for common authentication patterns:

src/composables/useAuthenticatedUser.ts

```typescript
import { computed, ref, watchEffect } from 'vue'
import { useAuth0 } from '@auth0/auth0-vue'

export function useAuthenticatedUser() {
  const { user, isAuthenticated, isLoading, getAccessTokenSilently } = useAuth0()
  const accessToken = ref<string | null>(null)

watchEffect(async () => {
    if (isAuthenticated.value && !isLoading.value) {
      try {
        accessToken.value = await getAccessTokenSilently()
      } catch (error) {
        console.error('Failed to get access token:', error)
      }
    }
  })

return {
    user: computed(() => user.value),
    accessToken: computed(() => accessToken.value),
    isAuthenticated: computed(() => isAuthenticated.value),
    isLoading: computed(() => isLoading.value)
  }
}
```

**Usage in components:**

src/components/UserDashboard.vue

```vue
<template>
  <div v-if="!isLoading">
    <h1>Welcome, {{ user?.name }}</h1>
    <p>Access Token: {{ accessToken ? 'Available' : 'Not available' }}</p>
  </div>
  <div v-else>Loading...</div>
</template>

<script setup lang="ts">
import { useAuthenticatedUser } from '../composables/useAuthenticatedUser'

const { user, accessToken, isLoading } = useAuthenticatedUser()
</script>
```
