Esta guía de inicio rápido se encuentra actualmente en beta. ¡Nos encantaría conocer tu opinión!
Usa IA para integrar Auth0
Usa IA para integrar Auth0
Si usas un asistente de programación con IA como Claude Code, Cursor o GitHub Copilot, puedes añadir la autenticación con Auth0 automáticamente en cuestión de minutos usando agent skills.Instala:Luego pídele a tu asistente de IA:Tu asistente de IA creará automáticamente tu aplicación en Auth0, obtendrá las credenciales, instalará
npx skills add https://github.com/auth0/agent-skills --skill auth0-nuxt
Add Auth0 authentication to my Nuxt app
@auth0/auth0-nuxt, configurará el módulo y tus rutas. Documentación completa de agent skills →Requisitos previos: Antes de comenzar, asegúrate de tener instalado lo siguiente:
- Node.js 20 LTS o una versión posterior
- npm 10+ o yarn 1.22+ o pnpm 8+
- jq - Necesario para configurar Auth0 CLI
Primeros pasos
1
Crear un proyecto
Crea un nuevo proyecto de Nuxt para esta guía de inicio rápidoAbre el proyecto
npx nuxi@latest init auth0-nuxt-app
cd auth0-nuxt-app
2
Instala el SDK de Auth0 para Nuxt
npm add @auth0/auth0-nuxt && npm install
3
Configura tu aplicación de Auth0
A continuación, debes crear una nueva aplicación en tu tenant de Auth0 y agregar las variables de entorno a tu proyecto.Puede hacerlo automáticamente ejecutando un comando de CLI o de forma manual a través del Dashboard:
- CLI
- Dashboard
Ejecuta el siguiente comando de shell en el directorio raíz de tu proyecto para crear una aplicación de Auth0 y generar un archivo
.env:AUTH0_APP_NAME="My Nuxt App" && brew tap auth0/auth0-cli && brew install auth0 && auth0 login --no-input && auth0 apps create -n "${AUTH0_APP_NAME}" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json > auth0-app-details.json && CLIENT_ID=$(jq -r '.client_id' auth0-app-details.json) && CLIENT_SECRET=$(jq -r '.client_secret' auth0-app-details.json) && DOMAIN=$(auth0 tenants list --json | jq -r '.[] | select(.active == true) | .name') && echo "NUXT_AUTH0_DOMAIN=${DOMAIN}" > .env && echo "NUXT_AUTH0_CLIENT_ID=${CLIENT_ID}" >> .env && echo "NUXT_AUTH0_CLIENT_SECRET=${CLIENT_SECRET}" >> .env && echo "NUXT_AUTH0_SESSION_SECRET=$(openssl rand -hex 64)" >> .env && echo "NUXT_AUTH0_APP_BASE_URL=http://localhost:3000" >> .env && rm auth0-app-details.json && echo ".env file created with your Auth0 details:" && cat .env
$AppName = "My Nuxt App"; winget install Auth0.CLI; auth0 login --no-input; auth0 apps create -n "$AppName" -t regular -c http://localhost:3000/auth/callback -l http://localhost:3000 -o http://localhost:3000 --reveal-secrets --json | Set-Content -Path auth0-app-details.json; $ClientId = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_id; $ClientSecret = (Get-Content -Raw auth0-app-details.json | ConvertFrom-Json).client_secret; $Domain = (auth0 tenants list --json | ConvertFrom-Json | Where-Object { $_.active -eq $true }).name; Set-Content -Path .env -Value "NUXT_AUTH0_DOMAIN=$Domain"; Add-Content -Path .env -Value "NUXT_AUTH0_CLIENT_ID=$ClientId"; Add-Content -Path .env -Value "NUXT_AUTH0_CLIENT_SECRET=$ClientSecret"; $SessionSecret = -join ((1..128) | ForEach {'{0:X}' -f (Get-Random -Max 16)}); Add-Content -Path .env -Value "NUXT_AUTH0_SESSION_SECRET=$SessionSecret"; Add-Content -Path .env -Value "NUXT_AUTH0_APP_BASE_URL=http://localhost:3000"; Remove-Item auth0-app-details.json; Write-Output ".env file created with your Auth0 details:"; Get-Content .env
Antes de empezar, crea un archivo URLs de cierre de sesión permitidas:Orígenes web permitidos:
.env en el directorio raíz de tu proyecto.env
NUXT_AUTH0_DOMAIN=YOUR_AUTH0_APP_DOMAIN
NUXT_AUTH0_CLIENT_ID=YOUR_AUTH0_APP_CLIENT_ID
NUXT_AUTH0_CLIENT_SECRET=YOUR_AUTH0_APP_CLIENT_SECRET
NUXT_AUTH0_SESSION_SECRET=YOUR_SESSION_SECRET
NUXT_AUTH0_APP_BASE_URL=http://localhost:3000
- Ve al Auth0 Dashboard
- Haz clic en Applications > Applications > Create Application
- En la ventana emergente, escribe un nombre para tu aplicación, selecciona
Regular Web Applicationcomo tipo de aplicación y haz clic en Create - Ve a la pestaña Settings en la página de detalles de la aplicación
- Reemplaza los valores del archivo
.envpor los de dominio, ID de cliente y Secreto del cliente del Auth0 Dashboard - Genera un secreto de sesión ejecutando:
openssl rand -hex 64y úsalo paraNUXT_AUTH0_SESSION_SECRET
http://localhost:3000/auth/callback
http://localhost:3000
http://localhost:3000
Allowed Callback URLs es una medida de seguridad fundamental para garantizar que los usuarios regresen de forma segura a su aplicación después de autenticarse. Sin una URL que coincida, el proceso de inicio de sesión fallará y los usuarios verán una página de error de Auth0 en lugar de acceder a su aplicación.Allowed Logout URLs es esencial para ofrecer una experiencia fluida al cerrar sesión. Sin una URL que coincida, los usuarios no serán redirigidos de vuelta a su aplicación después de cerrar sesión y, en su lugar, permanecerán en una página genérica de Auth0.Allowed Web Origins es fundamental para la autenticación silenciosa. Sin esta configuración, los usuarios cerrarán sesión al actualizar la página o al volver a su aplicación más tarde.
4
Configura el módulo Auth0 de Nuxt
nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
modules: ['@auth0/auth0-nuxt'],
runtimeConfig: {
auth0: {
domain: process.env.NUXT_AUTH0_DOMAIN,
clientId: process.env.NUXT_AUTH0_CLIENT_ID,
clientSecret: process.env.NUXT_AUTH0_CLIENT_SECRET,
sessionSecret: process.env.NUXT_AUTH0_SESSION_SECRET,
appBaseUrl: process.env.NUXT_AUTH0_APP_BASE_URL,
},
},
})
5
Crear componentes de inicio de sesión, cierre de sesión y perfil
Crear archivosY agrega los siguientes fragmentos de código
mkdir -p app/components && touch app/components/LoginButton.vue && touch app/components/LogoutButton.vue && touch app/components/UserProfile.vue
<template>
<a
href="/auth/login"
class="button login"
>
Log In
</a>
</template>
<style scoped>
.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.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:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
</style>
<template>
<a
href="/auth/logout"
class="button logout"
>
Log Out
</a>
</template>
<style scoped>
.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.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);
}
.button:disabled {
opacity: 0.7;
cursor: not-allowed;
transform: none;
}
</style>
<template>
<div v-if="pending" class="loading-text">
Loading profile...
</div>
<div v-else-if="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">
const user = useUser()
const pending = ref(false)
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
}
// Simular el estado de carga para mejorar la experiencia de usuario
onMounted(() => {
pending.value = true
nextTick(() => {
pending.value = false
})
})
</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;
}
.loading-text {
font-size: 1.8rem;
font-weight: 500;
color: #a0aec0;
animation: pulse 1.5s infinite ease-in-out;
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
</style>
<template>
<div class="app-container">
<div 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"
/>
<h1 class="main-title">Welcome to Auth0 Nuxt</h1>
<div v-if="user" 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">
const user = useUser()
</script>
<style>
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
body {
margin: 0;
font-family: 'Inter', sans-serif;
background-color: #1a1e27;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
color: #e2e8f0;
overflow: hidden;
}
.app-container {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-height: 100vh;
width: 100%;
padding: 1rem;
box-sizing: border-box;
}
.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;
}
.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;
}
.auth0-logo {
width: 120px;
}
}
</style>
6
Inicia tu aplicación
npm run dev
Punto de controlAhora debería tener una página de inicio de sesión de Auth0 completamente funcional ejecutándose en su localhost