41 lines
2.1 KiB
SQL
41 lines
2.1 KiB
SQL
-- ====================================================================
|
|
-- 0061_portal_api_tokens — Bearer-Token für die native App (iOS/Android)
|
|
-- ====================================================================
|
|
-- Die native App (React Native/Expo) kann keine Server Actions und kein
|
|
-- iron-session-Cookie nutzen. Sie authentifiziert über den bestehenden
|
|
-- Magic-Link-Code-Flow und erhält danach einen langlebigen Bearer-Token,
|
|
-- der im Gerät (Expo SecureStore / Keychain) liegt und bei jedem
|
|
-- /api/portal/*-Request im Authorization-Header mitgeschickt wird.
|
|
--
|
|
-- Identität = E-Mail (wie die Portal-Session, lib/portal-session.ts). Eine
|
|
-- Person kann mehrere aktive Karten/Profile mit derselben E-Mail haben;
|
|
-- active_employee_id hält das zuletzt gewählte Profil (bei Multi-Profil),
|
|
-- ist bei genau einem Profil bedeutungslos.
|
|
--
|
|
-- Sicherheit: Es wird NUR der SHA-256-Hash des Tokens gespeichert (wie bei
|
|
-- Admin-Invites) — ein DB-Leak gibt keine gültigen Tokens preis. Zugriff
|
|
-- ausschließlich über Service-Role (die Route-Handler), kein anon-Zugriff.
|
|
-- Gleitende Gültigkeit: expires_at rückt bei aktiver Nutzung nach vorne
|
|
-- (analog zur 90-Tage-Trusted-Portal-Session).
|
|
|
|
create table if not exists public.portal_api_tokens (
|
|
id uuid primary key default gen_random_uuid(),
|
|
email text not null,
|
|
token_hash text not null unique,
|
|
-- Zuletzt gewähltes Profil bei Multi-Profil. NULL = noch nicht gewählt
|
|
-- bzw. Single-Profil (dann löst der Server das eine aktive Profil auf).
|
|
active_employee_id uuid references public.employees(id) on delete set null,
|
|
user_agent text,
|
|
created_at timestamptz not null default now(),
|
|
last_used_at timestamptz not null default now(),
|
|
expires_at timestamptz not null,
|
|
revoked_at timestamptz
|
|
);
|
|
|
|
create index if not exists portal_api_tokens_email_idx
|
|
on public.portal_api_tokens (email);
|
|
|
|
-- RLS an, keine anon-Policy → Default-Deny für anon. Service-Role umgeht RLS.
|
|
alter table public.portal_api_tokens enable row level security;
|
|
revoke all on public.portal_api_tokens from anon;
|