Modul 10: Nuxt Ökosystem

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-CookieJWT oder Session-Cookie
before_action :authenticate_user!Route-Middleware
current_user HelperuseAuth() Composable
Devise-ViewsEigene 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.

composables/useAuth.ts
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 = 
server/api/auth/login.post.ts

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:

server/utils/session.ts
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 + OAuth-Handler
// 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:

middleware/auth.ts
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' })
pages/dashboard.vue
<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>
pages/login.vue
<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