Replace all marketing placeholder content with real MYeasyCMS content
Some checks failed
Workflow / ʦ TypeScript (push) Failing after 6m12s
Workflow / ⚫️ Test (push) Has been skipped

- Logo: Replace generic Makerkit SVG with MYeasyCMS branded logo (grid icon + styled text)
- Blog: Replace 3 SaaS placeholder posts with 5 real articles (Vereinsverwaltung, SEPA, Website, DSGVO, Mitglieder-Tipps)
- Changelog: Replace 6 generic entries with real feature announcements (Verbandsverwaltung, Fischerei, Dateien, Kurse, Einladungen, i18n)
- Documentation: Rewrite all 20 docs from Makerkit references to MYeasyCMS content
- FAQ: Replace 6 generic SaaS questions with 10 real MYeasyCMS questions
- Navigation: Replace Changelog link with Contact in main nav
- Footer: Reorganize into Product/Company/Legal sections
- Translations: Update all EN marketing strings to match real Com.BISS content
This commit is contained in:
Zaid Marzguioui
2026-04-01 21:09:06 +02:00
parent bbb33aa63d
commit a5bbf42901
49 changed files with 1320 additions and 4735 deletions

View File

@@ -1,25 +1,37 @@
---
title: "Authentication Overview"
description: "Learn how to set up authentication in your MakerKit application."
title: "Benutzerkonten"
description: "Benutzerkonten in MYeasyCMS — Registrierung, Login und Profilverwaltung."
publishedAt: 2024-04-11
order: 1
order: 0
status: "published"
---
MakerKit uses Supabase to manage authentication within your application.
Jeder Benutzer in MYeasyCMS hat ein persönliches Konto mit eigenen Zugangsdaten und Berechtigungen.
By default, every kit comes with the following built-in authentication methods:
- **Email/Password** - we added, by default, the traditional way of signing in
- **Third Party Providers** - we also added by default Google Auth sign-in
- **Email Links**
- **Phone Number**
## Anmeldung
You're free to add (or remove) any of the methods supported by Supabase's
Authentication: we will see how.
Melden Sie sich mit Ihrer E-Mail-Adresse und Ihrem Passwort an. Beim ersten Login werden Sie aufgefordert, Ihr Passwort zu ändern.
This documentation will help you with the following:
- **Setup** - setting up your Supabase project
- **SSR** - use SSR to persist your users' authentication, adding new
providers
- **Customization** - an overview of how MakerKit works so that you can adapt
it to your own application's needs
## Rollen
Jedem Benutzer wird eine Rolle zugewiesen, die seinen Zugriff steuert:
- **Administrator** — Vollzugriff auf alle Module und Einstellungen
- **Kassenwart** — Finanzen, Beiträge, Mitgliederdaten
- **Kursleiter** — Kurse, Teilnehmer
- **Schriftführer** — Protokolle, Dokumente
- **Mitglied** — Zugriff über das Mitgliederportal (eingeschränkt)
## Profil
Unter **Profil** können Sie Ihre persönlichen Daten aktualisieren:
- Name und Kontaktdaten
- E-Mail-Adresse
- Passwort ändern
- Sprache wählen (Deutsch / Englisch)
- Multi-Faktor-Authentifizierung aktivieren
## Multi-Faktor-Authentifizierung (MFA)
Für zusätzliche Sicherheit können Sie MFA aktivieren. Bei der Anmeldung wird dann neben dem Passwort ein zusätzlicher Code abgefragt — über eine Authenticator-App auf Ihrem Smartphone.

View File

@@ -1,55 +1,28 @@
---
title: "Configuration"
description: "Learn how authentication works in MakerKit and how to configure it."
title: "Anmeldung konfigurieren"
description: "Login-Optionen und Sicherheitseinstellungen in MYeasyCMS konfigurieren."
publishedAt: 2024-04-11
order: 1
status: "published"
---
The way you want your users to authenticate can be driven via configuration.
MYeasyCMS bietet verschiedene Anmeldemethoden, die vom Administrator konfiguriert werden können.
If you open the global configuration at `src/configuration.ts`, you'll find
the `auth` object:
## E-Mail und Passwort
```tsx title="configuration.ts"
import type { Provider } from '@supabase/gotrue-js/src/lib/types';
Die Standard-Anmeldung erfolgt mit E-Mail-Adresse und Passwort. Passwörter müssen eine Mindestlänge von 8 Zeichen haben und sollten Groß- und Kleinbuchstaben sowie Zahlen enthalten.
auth: {
requireEmailConfirmation: false,
providers: {
emailPassword: true,
phoneNumber: false,
emailLink: false,
oAuth: ['google'] as Provider[],
},
}
```
## Passwort zurücksetzen
As you can see, the `providers` object can be configured to only display the
auth methods we want to use.
Benutzer können ihr Passwort über die "Passwort vergessen"-Funktion zurücksetzen. Ein Link zur Passworterstellung wird per E-Mail zugesandt.
1. For example, by setting both `phoneNumber` and `emailLink` to `true`, the
authentication pages will display the `Email Link` authentication
and the `Phone Number` authentication forms.
2. Instead, by setting `emailPassword` to `false`, we will remove the
`email/password` form from the authentication and user profile pages.
## Multi-Faktor-Authentifizierung
## Requiring Email Verification
Für zusätzliche Sicherheit kann MFA aktiviert werden. Unterstützt werden:
This setting needs to match what you have set up in Supabase. If you require email confirmation before your users can sign in, you will have to flip the following flag in your configuration:
- **Authenticator-Apps** — Google Authenticator, Authy oder vergleichbare TOTP-Apps
- **Passkeys** — Biometrische Anmeldung über Fingerabdruck oder Gesichtserkennung
```ts
auth: {
requireEmailConfirmation: false,
}
```
## Sitzungsdauer
When the flag is set to `true`, the user will not be redirected to the onboarding flow, but will instead see a successful alert asking them to confirm their email. After confirmation, they will be able to sign in.
When the flag is set to `false`, the application will redirect them directly to the onboarding flow.
## Emails sent by Supabase
Supabase spins up an [InBucket](http://localhost:54324/) instance where all the emails are sent: this is where you can find emails related to password reset, sign-in links, and email verifications.
To access the InBucket instance, you can go to the following URL: [http://localhost:54324/](http://localhost:54324/). Save this URL, you will use it very often.
Anmeldesitzungen laufen nach einer konfigurierbaren Zeitspanne ab. Benutzer werden automatisch abgemeldet, wenn sie längere Zeit inaktiv sind.

View File

@@ -1,382 +1,29 @@
---
title: "Email & Password"
description: "Traditional email and password authentication."
title: "E-Mail und Passwort"
description: "Anmeldung mit E-Mail-Adresse und Passwort — die Standard-Authentifizierung in MYeasyCMS."
publishedAt: 2024-04-11
order: 2
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
Die Anmeldung mit E-Mail und Passwort ist die Standard-Authentifizierungsmethode in MYeasyCMS.
Email and password authentication is the traditional way users sign up and sign in.
## Registrierung
## Overview
Neue Benutzer registrieren sich mit ihrer E-Mail-Adresse und einem selbst gewählten Passwort. Nach der Registrierung wird eine Bestätigungs-E-Mail versendet.
Email/password authentication provides:
- User registration with email verification
- Secure password storage
- Password reset functionality
- Session management
## Passwortanforderungen
## Sign Up Flow
- Mindestens 8 Zeichen
- Kombination aus Buchstaben und Zahlen empfohlen
- Das Passwort kann jederzeit in den Profileinstellungen geändert werden
### User Registration
## Passwort vergessen
```typescript
import { signUpAction } from '~/lib/auth/actions';
Über die "Passwort vergessen"-Funktion auf der Login-Seite kann ein neues Passwort angefordert werden. Der Benutzer erhält einen Link per E-Mail, um ein neues Passwort festzulegen.
const result = await signUpAction({
email: 'user@example.com',
password: 'SecurePassword123!',
});
```
## Sicherheitshinweise
### Server Action Implementation
```typescript
'use server';
import { enhanceAction } from '@kit/next/actions';
import * as z from 'zod';
const SignUpSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export const signUpAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const { data: authData, error } = await client.auth.signUp({
email: data.email,
password: data.password,
options: {
emailRedirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/callback`,
},
});
if (error) throw error;
return { success: true, data: authData };
},
{ schema: SignUpSchema }
);
```
### Sign Up Component
```tsx
'use client';
import { useForm } from 'react-hook-form';
import { signUpAction } from '../_lib/actions';
export function SignUpForm() {
const { register, handleSubmit, formState: { errors } } = useForm();
const onSubmit = async (data) => {
const result = await signUpAction(data);
if (result.success) {
toast.success('Check your email to confirm your account');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Email</label>
<input
type="email"
{...register('email', { required: true })}
/>
{errors.email && <span>Email is required</span>}
</div>
<div>
<label>Password</label>
<input
type="password"
{...register('password', { required: true, minLength: 8 })}
/>
{errors.password && <span>Password must be 8+ characters</span>}
</div>
<button type="submit">Sign Up</button>
</form>
);
}
```
## Sign In Flow
### User Login
```typescript
export const signInAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const { error } = await client.auth.signInWithPassword({
email: data.email,
password: data.password,
});
if (error) throw error;
redirect('/home');
},
{ schema: SignInSchema }
);
```
### Sign In Component
```tsx
'use client';
export function SignInForm() {
const { register, handleSubmit } = useForm();
const onSubmit = async (data) => {
try {
await signInAction(data);
} catch (error) {
toast.error('Invalid email or password');
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="email"
{...register('email')}
placeholder="Email"
/>
<input
type="password"
{...register('password')}
placeholder="Password"
/>
<button type="submit">Sign In</button>
</form>
);
}
```
## Email Verification
### Requiring Email Confirmation
Configure in Supabase dashboard or config:
```typescript
// config/auth.config.ts
export const authConfig = {
requireEmailConfirmation: true,
};
```
### Handling Unconfirmed Emails
```typescript
export const signInAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const { data: authData, error } = await client.auth.signInWithPassword({
email: data.email,
password: data.password,
});
if (error) {
if (error.message.includes('Email not confirmed')) {
return {
success: false,
error: 'Please confirm your email before signing in',
};
}
throw error;
}
redirect('/home');
},
{ schema: SignInSchema }
);
```
## Password Reset
### Request Password Reset
```typescript
export const requestPasswordResetAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const { error } = await client.auth.resetPasswordForEmail(data.email, {
redirectTo: `${process.env.NEXT_PUBLIC_SITE_URL}/auth/reset-password`,
});
if (error) throw error;
return {
success: true,
message: 'Check your email for reset instructions',
};
},
{
schema: z.object({
email: z.string().email(),
}),
}
);
```
### Reset Password Form
```tsx
'use client';
export function PasswordResetRequestForm() {
const { register, handleSubmit } = useForm();
const onSubmit = async (data) => {
const result = await requestPasswordResetAction(data);
if (result.success) {
toast.success(result.message);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<input
type="email"
{...register('email')}
placeholder="Enter your email"
/>
<button type="submit">Send Reset Link</button>
</form>
);
}
```
### Update Password
```typescript
export const updatePasswordAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const { error } = await client.auth.updateUser({
password: data.newPassword,
});
if (error) throw error;
redirect('/home');
},
{
schema: z.object({
newPassword: z.string().min(8),
}),
}
);
```
## Password Requirements
### Validation Schema
```typescript
const PasswordSchema = z
.string()
.min(8, 'Password must be at least 8 characters')
.regex(/[A-Z]/, 'Password must contain an uppercase letter')
.regex(/[a-z]/, 'Password must contain a lowercase letter')
.regex(/[0-9]/, 'Password must contain a number')
.regex(/[^A-Za-z0-9]/, 'Password must contain a special character');
```
### Password Strength Indicator
```tsx
'use client';
import { useState } from 'react';
export function PasswordInput() {
const [password, setPassword] = useState('');
const strength = calculatePasswordStrength(password);
return (
<div>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<div className="flex gap-1">
{[1, 2, 3, 4].map((level) => (
<div
key={level}
className={cn(
'h-1 flex-1 rounded',
strength >= level ? 'bg-green-500' : 'bg-gray-200'
)}
/>
))}
</div>
<span className="text-sm">
{strength === 4 && 'Strong password'}
{strength === 3 && 'Good password'}
{strength === 2 && 'Fair password'}
{strength === 1 && 'Weak password'}
</span>
</div>
);
}
```
## Session Management
### Checking Authentication Status
```typescript
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export async function requireAuth() {
const client = getSupabaseServerClient();
const { data: { user } } = await client.auth.getUser();
if (!user) {
redirect('/auth/sign-in');
}
return user;
}
```
### Sign Out
```typescript
export const signOutAction = enhanceAction(
async () => {
const client = getSupabaseServerClient();
await client.auth.signOut();
redirect('/auth/sign-in');
}
);
```
## Security Best Practices
1. **Enforce strong passwords** - Minimum 8 characters, mixed case, numbers, symbols
2. **Rate limit login attempts** - Prevent brute force attacks
3. **Use HTTPS only** - Encrypt data in transit
4. **Enable email verification** - Confirm email ownership
5. **Implement account lockout** - After failed attempts
6. **Log authentication events** - Track sign-ins and failures
7. **Support 2FA** - Add extra security layer
- Verwenden Sie ein einzigartiges Passwort für MYeasyCMS
- Geben Sie Ihre Zugangsdaten nicht an andere weiter
- Aktivieren Sie die Multi-Faktor-Authentifizierung für zusätzliche Sicherheit

View File

@@ -1,392 +1,30 @@
---
title: "Magic Links"
description: "Passwordless authentication with email magic links."
title: "Einladungslinks"
description: "Benutzer per E-Mail-Einladung in den Vereins-Account einladen."
publishedAt: 2024-04-11
order: 4
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
Administratoren können neue Benutzer per Einladungslink in den Vereins-Account einladen.
Magic links provide passwordless authentication by sending a one-time link to the user's email.
## So funktioniert die Einladung
## How It Works
1. Navigieren Sie zu **Einstellungen → Mitarbeiter → Einladen**
2. Geben Sie die E-Mail-Adresse des neuen Benutzers ein
3. Wählen Sie die Rolle (Administrator, Kassenwart, Kursleiter, etc.)
4. Klicken Sie auf **Einladung senden**
1. User enters their email address
2. System sends an email with a unique link
3. User clicks the link in their email
4. User is automatically signed in
Der eingeladene Benutzer erhält eine E-Mail mit einem Registrierungslink. Nach der Registrierung hat er sofort Zugriff auf die Funktionen, die seiner Rolle entsprechen.
## Benefits
## Einladungen verwalten
- **No password to remember** - Better UX
- **More secure** - No password to steal
- **Lower friction** - Faster sign-up process
- **Email verification** - Confirms email ownership
Unter **Mitglieder → Einladungen** sehen Sie alle offenen und angenommenen Einladungen:
## Implementation
- **Offen** — Einladung wurde versendet, aber noch nicht angenommen
- **Angenommen** — Benutzer hat die Einladung angenommen und sein Konto erstellt
- **Abgelaufen** — Einladungslink ist nicht mehr gültig (kann erneut gesendet werden)
### Magic Link Form
## Erneut einladen
```tsx
'use client';
import { useForm } from 'react-hook-form';
import { sendMagicLinkAction } from '../_lib/actions';
export function MagicLinkForm() {
const { register, handleSubmit, formState: { isSubmitting } } = useForm();
const [sent, setSent] = useState(false);
const onSubmit = async (data) => {
const result = await sendMagicLinkAction(data);
if (result.success) {
setSent(true);
}
};
if (sent) {
return (
<div className="text-center">
<h2>Check your email</h2>
<p>We've sent you a magic link to sign in.</p>
</div>
);
}
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Email address</label>
<input
type="email"
{...register('email', { required: true })}
placeholder="you@example.com"
/>
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send magic link'}
</button>
</form>
);
}
```
### Server Action
```typescript
'use server';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import * as z from 'zod';
export const sendMagicLinkAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const origin = process.env.NEXT_PUBLIC_SITE_URL!;
const { error } = await client.auth.signInWithOtp({
email: data.email,
options: {
emailRedirectTo: `${origin}/auth/callback`,
shouldCreateUser: true,
},
});
if (error) throw error;
return {
success: true,
message: 'Check your email for the magic link',
};
},
{
schema: z.object({
email: z.string().email(),
}),
}
);
```
## Configuration
### Enable in Supabase
1. Go to **Authentication** → **Providers** → **Email**
2. Enable "Enable Email Provider"
3. Enable "Enable Email Confirmations"
### Configure Email Template
Customize the magic link email in Supabase Dashboard:
1. Go to **Authentication** → **Email Templates**
2. Select "Magic Link"
3. Customize the template:
```html
<h2>Sign in to {{ .SiteURL }}</h2>
<p>Click the link below to sign in:</p>
<p><a href="{{ .ConfirmationURL }}">Sign in</a></p>
<p>This link expires in {{ .TokenExpiryHours }} hours.</p>
```
## Callback Handler
Handle the magic link callback:
```typescript
// app/auth/callback/route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const requestUrl = new URL(request.url);
const token_hash = requestUrl.searchParams.get('token_hash');
const type = requestUrl.searchParams.get('type');
if (token_hash && type === 'magiclink') {
const cookieStore = cookies();
const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
const { error } = await supabase.auth.verifyOtp({
token_hash,
type: 'magiclink',
});
if (!error) {
return NextResponse.redirect(new URL('/home', request.url));
}
}
// Return error if verification failed
return NextResponse.redirect(
new URL('/auth/sign-in?error=invalid_link', request.url)
);
}
```
## Advanced Features
### Custom Redirect
Specify where users go after clicking the link:
```typescript
await client.auth.signInWithOtp({
email: data.email,
options: {
emailRedirectTo: `${origin}/onboarding`,
},
});
```
### Disable Auto Sign-Up
Require users to sign up first:
```typescript
await client.auth.signInWithOtp({
email: data.email,
options: {
shouldCreateUser: false, // Don't create new users
},
});
```
### Token Expiry
Configure link expiration (default: 1 hour):
```sql
-- In Supabase SQL Editor
ALTER TABLE auth.users
SET default_token_lifetime = '15 minutes';
```
## Rate Limiting
Prevent abuse by rate limiting magic link requests:
```typescript
import { ratelimit } from '~/lib/rate-limit';
export const sendMagicLinkAction = enhanceAction(
async (data, user, request) => {
// Rate limit by IP
const ip = request.headers.get('x-forwarded-for') || 'unknown';
const { success } = await ratelimit.limit(ip);
if (!success) {
throw new Error('Too many requests. Please try again later.');
}
const client = getSupabaseServerClient();
await client.auth.signInWithOtp({
email: data.email,
});
return { success: true };
},
{ schema: EmailSchema }
);
```
## Security Considerations
### Link Expiration
Magic links should expire quickly:
- Default: 1 hour
- Recommended: 15-30 minutes for production
- Shorter for sensitive actions
### One-Time Use
Links should be invalidated after use:
```typescript
// Supabase handles this automatically
// Each link can only be used once
```
### Email Verification
Ensure emails are verified:
```typescript
const { data: { user } } = await client.auth.getUser();
if (!user.email_confirmed_at) {
redirect('/verify-email');
}
```
## User Experience
### Loading State
Show feedback while sending:
```tsx
export function MagicLinkForm() {
const [status, setStatus] = useState<'idle' | 'sending' | 'sent'>('idle');
const onSubmit = async (data) => {
setStatus('sending');
await sendMagicLinkAction(data);
setStatus('sent');
};
return (
<>
{status === 'idle' && <EmailForm onSubmit={onSubmit} />}
{status === 'sending' && <SendingMessage />}
{status === 'sent' && <CheckEmailMessage />}
</>
);
}
```
### Resend Link
Allow users to request a new link:
```tsx
export function ResendMagicLink({ email }: { email: string }) {
const [canResend, setCanResend] = useState(false);
const [countdown, setCountdown] = useState(60);
useEffect(() => {
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
} else {
setCanResend(true);
}
}, [countdown]);
const handleResend = async () => {
await sendMagicLinkAction({ email });
setCountdown(60);
setCanResend(false);
};
return (
<button onClick={handleResend} disabled={!canResend}>
{canResend ? 'Resend link' : `Resend in ${countdown}s`}
</button>
);
}
```
## Email Deliverability
### SPF, DKIM, DMARC
Configure email authentication:
1. Add SPF record to DNS
2. Enable DKIM signing
3. Set up DMARC policy
### Custom Email Domain
Use your own domain for better deliverability:
1. Go to **Project Settings** → **Auth**
2. Configure custom SMTP
3. Verify domain ownership
### Monitor Bounces
Track email delivery issues:
```typescript
// Handle email bounces
export async function handleEmailBounce(email: string) {
await client.from('email_bounces').insert({
email,
bounced_at: new Date(),
});
// Notify user via other channel
}
```
## Testing
### Local Development
In development, emails go to InBucket:
```
http://localhost:54324
```
Check this URL to see magic link emails during testing.
### Test Mode
Create a test link without sending email:
```typescript
if (process.env.NODE_ENV === 'development') {
console.log('Magic link URL:', confirmationUrl);
}
```
## Best Practices
1. **Clear communication** - Tell users to check spam
2. **Short expiry** - 15-30 minutes for security
3. **Rate limiting** - Prevent abuse
4. **Fallback option** - Offer password auth as backup
5. **Custom domain** - Better deliverability
6. **Monitor delivery** - Track bounces and failures
7. **Resend option** - Let users request new link
8. **Mobile-friendly** - Ensure links work on mobile
Abgelaufene oder nicht angenommene Einladungen können mit einem Klick erneut versendet werden.

View File

@@ -1,395 +1,31 @@
---
title: "OAuth"
description: "Sign in with Google, GitHub, and other OAuth providers."
title: "Mitgliederportal"
description: "Das Mitgliederportal ermöglicht Vereinsmitgliedern den Zugriff auf ihre eigenen Daten."
publishedAt: 2024-04-11
order: 3
status: "published"
---
> **Note:** This is mock/placeholder content for demonstration purposes.
Das Mitgliederportal ist der Bereich von MYeasyCMS, der für die Vereinsmitglieder selbst zugänglich ist.
Allow users to sign in with their existing accounts from Google, GitHub, and other providers.
## Funktionen für Mitglieder
## Supported Providers
Über das Portal können Mitglieder:
Supabase supports many OAuth providers:
- Google
- GitHub
- GitLab
- Bitbucket
- Azure
- Facebook
- Twitter
- Discord
- Slack
- And more...
- **Stammdaten einsehen und aktualisieren** — Adresse, Telefonnummer, E-Mail
- **Kursanmeldungen** — Sich für Kurse und Veranstaltungen anmelden
- **Beitragshistorie** — Ihre Zahlungshistorie einsehen
- **Dokumente** — Persönliche Dokumente herunterladen (Ausweis, Bestätigungen)
- **Newsletter** — Newsletter-Abonnement verwalten
## Setting Up OAuth
## Zugang einrichten
### Configure in Supabase Dashboard
Mitglieder erhalten Zugang zum Portal über die Vereinswebsite. Der Login erfolgt mit E-Mail-Adresse und Passwort.
1. Go to **Authentication** → **Providers**
2. Enable your desired provider (e.g., Google)
3. Add your OAuth credentials:
- **Client ID**
- **Client Secret**
- **Redirect URL**: `https://your-project.supabase.co/auth/v1/callback`
## Datenschutz
### Google OAuth Setup
Jedes Mitglied sieht ausschließlich seine eigenen Daten. Ein Zugriff auf andere Mitgliederdaten oder Verwaltungsfunktionen ist nicht möglich.
1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project or select existing
3. Enable Google+ API
4. Create OAuth 2.0 credentials
5. Add authorized redirect URIs:
- Production: `https://your-project.supabase.co/auth/v1/callback`
- Development: `http://localhost:54321/auth/v1/callback`
## Kontoverknüpfung
### GitHub OAuth Setup
1. Go to GitHub Settings → Developer Settings → OAuth Apps
2. Click "New OAuth App"
3. Fill in details:
- **Application name**: Your App
- **Homepage URL**: `https://yourapp.com`
- **Authorization callback URL**: `https://your-project.supabase.co/auth/v1/callback`
4. Copy Client ID and Client Secret to Supabase
## Implementation
### OAuth Sign In Button
```tsx
'use client';
import { signInWithOAuthAction } from '../_lib/actions';
export function OAuthButtons() {
const handleGoogleSignIn = async () => {
await signInWithOAuthAction('google');
};
const handleGitHubSignIn = async () => {
await signInWithOAuthAction('github');
};
return (
<div className="space-y-2">
<button
onClick={handleGoogleSignIn}
className="w-full flex items-center justify-center gap-2 border rounded-lg p-2"
>
<GoogleIcon />
Continue with Google
</button>
<button
onClick={handleGitHubSignIn}
className="w-full flex items-center justify-center gap-2 border rounded-lg p-2"
>
<GitHubIcon />
Continue with GitHub
</button>
</div>
);
}
```
### Server Action
```typescript
'use server';
import { enhanceAction } from '@kit/next/actions';
import { getSupabaseServerClient } from '@kit/supabase/server-client';
import * as z from 'zod';
const OAuthProviderSchema = z.enum([
'google',
'github',
'gitlab',
'azure',
'facebook',
]);
export const signInWithOAuthAction = enhanceAction(
async (provider) => {
const client = getSupabaseServerClient();
const origin = process.env.NEXT_PUBLIC_SITE_URL!;
const { data, error } = await client.auth.signInWithOAuth({
provider,
options: {
redirectTo: `${origin}/auth/callback`,
},
});
if (error) throw error;
// Redirect to OAuth provider
redirect(data.url);
},
{
schema: OAuthProviderSchema,
}
);
```
### OAuth Callback Handler
```typescript
// app/auth/callback/route.ts
import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';
export async function GET(request: Request) {
const requestUrl = new URL(request.url);
const code = requestUrl.searchParams.get('code');
if (code) {
const cookieStore = cookies();
const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
await supabase.auth.exchangeCodeForSession(code);
}
// Redirect to home page
return NextResponse.redirect(new URL('/home', request.url));
}
```
## Customizing OAuth Flow
### Scopes
Request specific permissions:
```typescript
await client.auth.signInWithOAuth({
provider: 'google',
options: {
scopes: 'email profile https://www.googleapis.com/auth/calendar',
},
});
```
### Query Parameters
Pass custom parameters:
```typescript
await client.auth.signInWithOAuth({
provider: 'azure',
options: {
queryParams: {
prompt: 'consent',
access_type: 'offline',
},
},
});
```
### Skip Browser Redirect
For mobile apps or custom flows:
```typescript
const { data } = await client.auth.signInWithOAuth({
provider: 'google',
options: {
skipBrowserRedirect: true,
},
});
// data.url contains the OAuth URL
// Handle redirect manually
```
## Account Linking
### Linking Additional Providers
Allow users to link multiple OAuth accounts:
```typescript
export const linkOAuthProviderAction = enhanceAction(
async (provider) => {
const client = getSupabaseServerClient();
const user = await requireAuth();
const { data, error } = await client.auth.linkIdentity({
provider,
});
if (error) throw error;
redirect(data.url);
},
{ schema: OAuthProviderSchema, auth: true }
);
```
### Unlinking Providers
```typescript
export const unlinkOAuthProviderAction = enhanceAction(
async ({ provider, identityId }) => {
const client = getSupabaseServerClient();
const { error } = await client.auth.unlinkIdentity({
identity_id: identityId,
});
if (error) throw error;
revalidatePath('/settings/security');
},
{
schema: z.object({
provider: z.string(),
identityId: z.string(),
}),
auth: true,
}
);
```
### Viewing Linked Identities
```typescript
import { getSupabaseServerClient } from '@kit/supabase/server-client';
export async function getLinkedIdentities() {
const client = getSupabaseServerClient();
const { data: { user } } = await client.auth.getUser();
return user?.identities || [];
}
```
## User Data from OAuth
### Accessing Provider Data
```typescript
const { data: { user } } = await client.auth.getUser();
// User metadata from provider
const {
full_name,
avatar_url,
email,
} = user.user_metadata;
// Provider-specific data
const identities = user.identities || [];
const googleIdentity = identities.find(i => i.provider === 'google');
console.log(googleIdentity?.identity_data);
```
### Storing Additional Data
```typescript
export const completeOAuthProfileAction = enhanceAction(
async (data) => {
const client = getSupabaseServerClient();
const user = await requireAuth();
// Update user metadata
await client.auth.updateUser({
data: {
username: data.username,
bio: data.bio,
},
});
// Update profile in database
await client.from('profiles').upsert({
id: user.id,
username: data.username,
bio: data.bio,
avatar_url: user.user_metadata.avatar_url,
});
redirect('/home');
},
{ schema: ProfileSchema, auth: true }
);
```
## Configuration
### Enable OAuth in Config
```typescript
// config/auth.config.ts
export const authConfig = {
providers: {
emailPassword: true,
oAuth: ['google', 'github'],
},
};
```
### Conditional Rendering
```tsx
import { authConfig } from '~/config/auth.config';
export function AuthProviders() {
return (
<>
{authConfig.providers.emailPassword && <EmailPasswordForm />}
{authConfig.providers.oAuth?.includes('google') && (
<GoogleSignInButton />
)}
{authConfig.providers.oAuth?.includes('github') && (
<GitHubSignInButton />
)}
</>
);
}
```
## Troubleshooting
### Redirect URI Mismatch
Ensure redirect URIs match exactly:
- Check Supabase Dashboard → Authentication → URL Configuration
- Verify OAuth app settings in provider console
- Use exact URLs (including http/https)
### Missing Email
Some providers don't share email by default:
```typescript
const { data: { user } } = await client.auth.getUser();
if (!user.email) {
// Request email separately or prompt user
redirect('/auth/complete-profile');
}
```
### Rate Limiting
OAuth providers may rate limit requests:
- Cache OAuth tokens appropriately
- Don't make excessive authorization requests
- Handle rate limit errors gracefully
## Best Practices
1. **Request minimum scopes** - Only ask for what you need
2. **Handle errors gracefully** - OAuth can fail for many reasons
3. **Verify email addresses** - Some providers don't verify emails
4. **Support account linking** - Let users connect multiple providers
5. **Provide fallback** - Always offer email/password as backup
6. **Log OAuth events** - Track sign-ins and linking attempts
7. **Test thoroughly** - Test with real provider accounts
Mitglieder, die in mehreren Vereinen aktiv sind, können ihre Konten verknüpfen und zwischen den Vereinen wechseln, ohne sich erneut anmelden zu müssen.