feat(auth): complete sqlite auth and client invitations
This commit is contained in:
@@ -1,39 +1,78 @@
|
||||
# Phase 2 Auth Implementation Notes
|
||||
---
|
||||
title: Phase 1 Auth and Client Invitation Implementation
|
||||
description: Better Auth, SQLite setup lock, session guards, client invitation lifecycle and auth audit implementation notes.
|
||||
status: complete
|
||||
last_updated: 2026-07-16
|
||||
---
|
||||
|
||||
## Kapsam
|
||||
# Phase 1 Auth and Client Invitation Implementation
|
||||
|
||||
Faz 2'de Supabase Auth yerine Better Auth + Drizzle SQLite temelli ilk auth katmanı eklendi. Bu faz, veri ekranlarının tamamını Supabase'ten taşımıyor; koruma noktalarını ve yeni session contract'ını hazır hale getiriyor.
|
||||
Bu dosya tarihsel adı korunarak Faz 1'de tamamlanan Better Auth + SQLite auth temelini kaydeder. Domain ekranlarının Supabase veri sorgularından taşınması Faz 2 ve sonraki domain fazlarının kapsamındadır; auth ve davet yaşam döngüsü artık Supabase Auth kullanmaz.
|
||||
|
||||
## Eklenen runtime parçaları
|
||||
## Tamamlanan runtime parçaları
|
||||
|
||||
- `server/auth/auth.ts`: Better Auth server-only config.
|
||||
- `app/api/auth/[...all]/route.ts`: Better Auth GET/POST Route Handler.
|
||||
- `server/db/schema/auth.ts`: Better Auth auth tabloları, Neta profile tablosu, setup lock, portal invitation ve audit log tabloları.
|
||||
- `server/auth/session.ts`: request içi memoize edilen `getSessionContext`, `requireSession`, `requireFreelancer`, `requireClientUser`.
|
||||
- `server/auth/setup.ts`: ilk freelancer setup durumu, atomic setup guard ve audit yazımı.
|
||||
- `server/auth/authorization.ts`: role ve owner assertion helper'ları.
|
||||
- `server/auth/auth.ts`: Better Auth config, first-owner user hook'ları ve session oluşturma audit/guard hook'ları.
|
||||
- `server/auth/setup.ts`: transaction korumalı ilk owner kilidi, stale repair, başarısız setup kilidi temizliği ve auth audit yazımı.
|
||||
- `server/auth/session.ts`: web ve Route Handler için session context, disabled profile ve role/client binding kontrolleri.
|
||||
- `server/auth/invitations.ts`: davet üretme, hash-only token, replacement/revoke, expiry, transaction içinde kabul ve client access enable/disable servisi.
|
||||
- `app/api/portal-invitations/*`: freelancer-only create/revoke ve public invitation accept adapter'ları.
|
||||
- `app/api/portal-clients/[clientId]`: client portal erişimi enable/disable adapter'ı.
|
||||
- `app/invite/[token]`: davet durumu ve müşterinin kendi şifresini belirlediği kabul ekranı.
|
||||
- `server/db/migrations/0002_mighty_korg.sql`: `app_profiles.client_id` identity binding ve unique index migration'ı.
|
||||
|
||||
## Güvenlik kararları
|
||||
## İlk owner ve public registration
|
||||
|
||||
- `BETTER_AUTH_SECRET` production runtime'da zorunludur. Build sırasında placeholder kullanılır; runtime'da env yoksa uygulama hata verir.
|
||||
- `TRUSTED_ORIGINS` wildcard kabul etmez.
|
||||
- Auth cookie'leri production'da `Secure`, tüm ortamlarda `HttpOnly`, `SameSite=Lax`, `Path=/` ayarlarıyla üretilir.
|
||||
- Public sign-up endpoint'i `databaseHooks.user.create.before` ile ilk freelancer setup guard'ına bağlıdır. İlk freelancer oluştuktan sonra doğrudan `/api/auth/sign-up/email` çağrısı da kullanıcı oluşturamaz.
|
||||
- Login hatası genel mesaj döndürür; email varlığı sızdırılmaz.
|
||||
- Public `/api/auth/sign-up/email`, ilk owner'dan sonra database hook seviyesinde kapanır; yalnızca `/register` UI kontrolüne dayanmaz.
|
||||
- Eşzamanlı ilk kayıt istekleri `app_setup_state` kilidiyle serialize edilir.
|
||||
- İlk başarılı kullanıcı `app_profiles.role = freelancer` olarak bağlanır.
|
||||
- Better Auth user insert ile session insert arasındaki hook sırası için stale repair çalışır; profile tamamlanmadan session verilmez.
|
||||
- Setup başarısızsa aynı e-postaya ait pending kilit temizlenir ve `setup_failed` audit olayı yazılır.
|
||||
|
||||
## Bilinen sınırlar
|
||||
## Client invitation sözleşmesi
|
||||
|
||||
- Portal client kullanıcı üretimi bu fazda sadece token modeli seviyesindedir; gerçek client invitation tüketimi sonraki veri/API fazında tamamlanacak.
|
||||
- Eski dashboard ve portal feature sayfalarının veri sorguları hâlâ Supabase kullanıyor. Layout koruması Better Auth'a taşındı, veri okuma/yazma Faz 4-6 kapsamındadır.
|
||||
- Reverse proxy/TLS altında cookie testi Docker daemon çalışmadığı için bu turda kapatılmadı.
|
||||
- Yalnızca aktif `freelancer` rolü davet oluşturabilir veya iptal edebilir.
|
||||
- Token `randomBytes(32)` ile üretilir; SQLite'ta yalnızca SHA-256 hash saklanır.
|
||||
- Varsayılan TTL 72 saat, servis üst sınırı 168 saattir.
|
||||
- Aynı `clientId` için yeni davet önceki pending davetleri revoke eder ve bu değişiklik audit edilir.
|
||||
- Kabul sırasında Better Auth `user`, credential `account`, `app_profiles` client kaydı, `client_id` identity bağı ve invitation `accepted` durumu tek SQLite transaction'ında yazılır.
|
||||
- Kullanılmış, değiştirilmiş, süresi dolmuş veya revoke edilmiş token yeniden kullanılamaz.
|
||||
- Disable işlemi profile'ı kapatır ve o client'ın aktif Better Auth session kayıtlarını aynı transaction'da siler.
|
||||
- Disabled veya `client_id` bağı eksik client, Better Auth endpoint'ini doğrudan çağırsa bile session oluşturamaz.
|
||||
|
||||
## Doğrulama
|
||||
`app_profiles.client_id`, Faz 1 auth sınırında opaque domain identity bağıdır. Yerel `clients` tablosu ve foreign key Faz 2 domain migration'ında eklenecektir; mevcut Supabase client sayfaları bu nedenle henüz domain açısından hibrittir.
|
||||
|
||||
- `npm run db:generate`
|
||||
- `npm run db:migrate`
|
||||
- `npm run phase2:smoke`
|
||||
- `npm run typecheck`
|
||||
- `npm run build`
|
||||
- `npm run lint`
|
||||
## Audit kapsamı
|
||||
|
||||
`npm run lint` mevcut proje baseline'ındaki eski hatalar nedeniyle başarısız kalabilir; Faz 2 dosyalarında yeni lint bulgusu bırakılmamalıdır.
|
||||
Kapsanan olaylar:
|
||||
|
||||
- setup start/completion/failure ve kapalı registration denemesi;
|
||||
- login success/failure ve logout;
|
||||
- invitation create/revoke/expire/accept/accept failure;
|
||||
- client access disable/enable.
|
||||
|
||||
Raw invitation token, parola, session token veya auth secret audit metadata'sına yazılmaz.
|
||||
|
||||
## Production güvenlik ve env
|
||||
|
||||
- Production runtime'da en az 32 karakter `BETTER_AUTH_SECRET` zorunludur.
|
||||
- `APP_URL`, `NEXT_PUBLIC_SITE_URL`, opsiyonel `TRUSTED_ORIGINS` ve secret Compose sözleşmesine eklenmiştir.
|
||||
- Wildcard trusted origin reddedilir.
|
||||
- Cookie'ler HTTPS deployment'ta `Secure`; tüm ortamlarda `HttpOnly`, `SameSite=Lax`, `Path=/` kullanır. Local Docker'ın `http://localhost` kurulumu kontrollü istisnadır; production'da localhost dışındaki HTTP `APP_URL` boot sırasında reddedilir.
|
||||
- Build worker'ları SQLite module initialization sırasında birbirini kilitlemesin diye production build her process için ayrı geçici data dizini kullanır. Runtime yolu değişmez: `/app/data`.
|
||||
|
||||
## Doğrulama — 2026-07-16
|
||||
|
||||
| Kontrol | Sonuç |
|
||||
| --- | --- |
|
||||
| `npm run typecheck` | Başarılı |
|
||||
| Değişen Faz 1 dosyalarında targeted ESLint | 0 error, 0 warning |
|
||||
| `node scripts/phase1-smoke.mjs` | Başarılı |
|
||||
| `node scripts/phase2-auth-smoke.mjs` | Başarılı |
|
||||
| `node scripts/phase1-auth-smoke.mjs` | Başarılı, ardışık iki çalışma |
|
||||
| `npm run build` | Başarılı, standalone route çıktısı üretildi |
|
||||
| `docker compose config` | Başarılı, secret ve URL env'leri çözüldü |
|
||||
| Docker image/runtime smoke | Çalıştırılamadı; yerel Docker daemon aktif değil |
|
||||
|
||||
Uçtan uca auth smoke şu senaryoları kapsar: concurrent first setup, kayıt kapanışı, owner login/logout, token'ın hash saklanması, replacement revoke, davet kabulü, replay reddi, expired/revoked token reddi, client→freelancer role ihlali, disable ile session revoke, disabled direct login reddi ve enable sonrası login.
|
||||
|
||||
Repo geneli lint, Faz 0'da kaydedilmiş eski feature dosyalarındaki baseline hatalar nedeniyle kalite kapısı olarak açık kalır. Faz 1'de değiştirilen dosyalarda yeni lint bulgusu yoktur.
|
||||
|
||||
Reference in New Issue
Block a user