Merge commit '80eca1bc7c1174f9c44a8df9af4ddc8ab855291b' as 'cms/core'
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
import { verify } from 'hono/jwt';
|
||||
import { supabaseAuth } from './supabase.js';
|
||||
|
||||
// Supabase-Tokens sind HS256-signiert. Mit dem JWT_SECRET verifizieren wir sie
|
||||
// lokal (Signatur + Ablauf) — das spart pro Request den Roundtrip zu GoTrue.
|
||||
// Ohne JWT_SECRET (z.B. Alt-Deploy) fällt requireAuth auf die Remote-Prüfung
|
||||
// zurück. Tokens sind kurzlebig (1h) und Self-Signup ist aus → kein
|
||||
// Sperr-Check nötig.
|
||||
const JWT_SECRET = process.env.JWT_SECRET || '';
|
||||
|
||||
// Liefert ein User-Objekt {id,email,app_metadata} oder null.
|
||||
async function verifyToken(token) {
|
||||
if (JWT_SECRET) {
|
||||
try {
|
||||
const p = await verify(token, JWT_SECRET, 'HS256');
|
||||
if (!p?.sub) return null;
|
||||
return { id: p.sub, email: p.email || '', app_metadata: p.app_metadata || {} };
|
||||
} catch { return null; }
|
||||
}
|
||||
const { data, error } = await supabaseAuth.auth.getUser(token);
|
||||
if (error || !data?.user) return null;
|
||||
return data.user;
|
||||
}
|
||||
|
||||
// Rollen-Hierarchie: admin > editor (Redakteur) > user.
|
||||
// - admin: alles (Foren verwalten, moderieren, Nutzer/Rollen, Inhalte)
|
||||
// - editor: moderieren (Wortmeldungen ausblenden/löschen, Threads sperren)
|
||||
// - user: im Forum mitschreiben
|
||||
// Admins aus der .env (ADMIN_EMAILS=a@x,b@y) sind immer Admin (Bootstrap, damit
|
||||
// man sich nicht aussperrt). Zusätzlich kann eine Rolle in app_metadata.role
|
||||
// liegen (im Admin-UI vergeben).
|
||||
const ADMINS = (process.env.ADMIN_EMAILS || '')
|
||||
.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean);
|
||||
|
||||
export function roleOf(user) {
|
||||
const email = (user?.email || '').toLowerCase();
|
||||
const meta = (user?.app_metadata?.role || '').toLowerCase();
|
||||
if (ADMINS.includes(email) || meta === 'admin') return 'admin';
|
||||
if (meta === 'editor') return 'editor';
|
||||
return 'user';
|
||||
}
|
||||
|
||||
// Verifiziert den Supabase-Access-Token und legt user/email/role im Kontext ab.
|
||||
export async function requireAuth(c, next) {
|
||||
const header = c.req.header('Authorization') || '';
|
||||
const token = header.startsWith('Bearer ') ? header.slice(7) : null;
|
||||
if (!token) return c.json({ error: 'Nicht eingeloggt' }, 401);
|
||||
|
||||
const user = await verifyToken(token);
|
||||
if (!user) return c.json({ error: 'Ungültiges Token' }, 401);
|
||||
|
||||
const email = (user.email || '').toLowerCase();
|
||||
const role = roleOf(user);
|
||||
c.set('user', user);
|
||||
c.set('email', email);
|
||||
c.set('role', role);
|
||||
c.set('isAdmin', role === 'admin');
|
||||
c.set('canModerate', role === 'admin' || role === 'editor');
|
||||
await next();
|
||||
}
|
||||
|
||||
// Nur Admins (nach requireAuth einsetzen).
|
||||
export async function requireAdmin(c, next) {
|
||||
if (!c.get('isAdmin')) return c.json({ error: 'Nur für Admins' }, 403);
|
||||
await next();
|
||||
}
|
||||
|
||||
// Admins + Redakteure — fürs Moderieren (nach requireAuth einsetzen).
|
||||
export async function requireModerator(c, next) {
|
||||
if (!c.get('canModerate')) return c.json({ error: 'Nur für Moderation' }, 403);
|
||||
await next();
|
||||
}
|
||||
Reference in New Issue
Block a user