Auth-Patterns
Authentifizierung und Autorisierung in Nuxt-Anwendungen
Authentifizierung in Nuxt
Anders als in Rails, wo du mit Devise eine fertige Auth-Lösung bekommst, baust du in Nuxt die Authentifizierung modularer auf. Es gibt verschiedene Patterns — von JWT über Sessions bis OAuth — die du je nach Backend-Architektur kombinierst.
Rails-Vergleich: Devise vs Nuxt Auth
Devise in Rails liefert alles aus einer Hand: Registrierung, Login, Passwort-Reset, Session-Management. In der Nuxt-Welt baust du diese Teile selbst zusammen — oder nutzt Bibliotheken wie sidebase/nuxt-auth. Der Vorteil: Volle Kontrolle über den Auth-Flow.
| Devise (Rails) | Nuxt-Äquivalent |
|---|---|
| Session-Cookie | JWT oder Session-Cookie |
| before_action :authenticate_user! | Route-Middleware |
| current_user Helper | useAuth() Composable |
| Devise-Views | Eigene Login/Register-Seiten |
JWT-basierte Authentifizierung
Das häufigste Pattern für SPAs und SSR-Apps: Der Server gibt ein JWT-Token zurück, das der Client bei jedem Request mitsendet.
interface User {
id: number
email: string
name: string
}
interface LoginCredentials {
email: string
password: string
}
export const useAuth = () => {
const user = useState<User | null>('auth-user', () => null)
const token = useCookie('auth-token', { maxAge: 60 * 60 * 24 * 7 }) // 7 Tage
const isLoggedIn = computed(() => !!user.value)
async function login(credentials: LoginCredentials) {
try {
const response = await \$fetch<{ user: User; token: string }>(
'/api/auth/login',
{
method: 'POST',
body: credentials
}
)
token.value = response.token
user.value = response.user
return { success: true }
} catch (error: any) {
return {
success: false,
error: error.data?.message || 'Login fehlgeschlagen'
}
}
}
async function logout() {
await \$fetch('/api/auth/logout', { method: 'POST' })
token.value = null
user.value = null
navigateTo('/login')
}
async function fetchUser() {
if (!token.value) return
try {
user.value = await \$fetch<User>('/api/auth/me', {
headers: { Authorization: \\\`
const codeExample2 = Session-basierte Authentifizierung
Für SSR-Apps kann eine Session-basierte Auth sinnvoller sein — ähnlich wie in Rails. Der Server speichert die Session, der Client bekommt ein Cookie:
import type { H3Event } from 'h3'
// Einfacher In-Memory Session-Store (in Produktion: Redis!)
const sessions = new Map<string, { userId: number; expiresAt: number }>()
export function createSession(event: H3Event, userId: number): string {
const sessionId = crypto.randomUUID()
const expiresAt = Date.now() + 1000 * 60 * 60 * 24 * 7 // 7 Tage
sessions.set(sessionId, { userId, expiresAt })
// HttpOnly-Cookie setzen
setCookie(event, 'session-id', sessionId, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7,
path: '/'
})
return sessionId
}
export function getSession(event: H3Event) {
const sessionId = getCookie(event, 'session-id')
if (!sessionId) return null
const session = sessions.get(sessionId)
if (!session || session.expiresAt < Date.now()) {
sessions.delete(sessionId!)
return null
}
return session
}
export function destroySession(event: H3Event) {
const sessionId = getCookie(event, 'session-id')
if (sessionId) {
sessions.delete(sessionId)
deleteCookie(event, 'session-id')
}
}OAuth-Integration
Für Social Login (GitHub, Google, etc.) empfiehlt sich das nuxt-auth-utils-Modul:
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['nuxt-auth-utils']
})
// server/routes/auth/github.get.ts
export default defineOAuthGitHubEventHandler({
config: {
emailRequired: true,
scope: ['user:email']
},
async onSuccess(event, { user, tokens }) {
// Session setzen
await setUserSession(event, {
user: {
id: user.id,
name: user.login,
email: user.email,
avatar: user.avatar_url
}
})
return sendRedirect(event, '/dashboard')
},
onError(event, error) {
console.error('GitHub OAuth Fehler:', error)
return sendRedirect(event, '/login?error=oauth')
}
})
// Verwendung in der Komponente:
// <a href="/auth/github">Mit GitHub anmelden</a>Middleware für geschützte Routen
Route-Middleware schützt Seiten vor unauthentifiziertem Zugriff — das Pendant zu before_action :authenticate_user! in Rails:
export default defineNuxtRouteMiddleware((to) => {
const { isLoggedIn } = useAuth()
if (!isLoggedIn.value) {
// Redirect zur Login-Seite mit Return-URL
return navigateTo({
path: '/login',
query: { redirect: to.fullPath }
})
}
})
// Verwendung in Seiten:
// definePageMeta({ middleware: 'auth' })<template>
<div>
<h1>Dashboard</h1>
<p>Willkommen, {{ user?.name }}!</p>
<button @click="logout">Abmelden</button>
</div>
</template>
<script setup lang="ts">
// Diese Seite ist nur für eingeloggte Nutzer
definePageMeta({ middleware: 'auth' })
const { user, logout } = useAuth()
</script><template>
<div class="login-form">
<h1>Anmelden</h1>
<form @submit.prevent="handleLogin">
<div>
<label for="email">E-Mail</label>
<input id="email" v-model="email" type="email" required />
</div>
<div>
<label for="password">Passwort</label>
<input id="password" v-model="password" type="password" required />
</div>
<p v-if="error" class="error">{{ error }}</p>
<button type="submit" :disabled="loading">
{{ loading ? 'Wird angemeldet...' : 'Anmelden' }}
</button>
</form>
</div>
</template>
<script setup lang="ts">
definePageMeta({ layout: 'blank' })
const { login } = useAuth()
const route = useRoute()
const email = ref('')
const password = ref('')
const error = ref('')
const loading = ref(false)
async function handleLogin() {
loading.value = true
error.value = ''
const result = await login({ email: email.value, password: password.value })
if (result.success) {
const redirect = (route.query.redirect as string) || '/dashboard'
navigateTo(redirect)
} else {
error.value = result.error
}
loading.value = false
}
</script>Zusammenfassung
Auth-Patterns im Überblick
- JWT — Token im Cookie, für API-basierte Backends
- Session — Serverseitige Sessions, wie in Rails
- OAuth — Social Login mit nuxt-auth-utils
- useAuth() — Composable für Auth-State im Client
- Middleware — Schützt Routen wie
before_action - useCookie() — SSR-sicherer Cookie-Zugriff