Aplikacje · Case study
NetovaTodo – projekt własny aplikacji z AES-256, OAuth i Passkeys
Projekt własny pokazujący moje podejście do aplikacji webowych: szyfrowanie AES-256, Passkeys/WebAuthn, OAuth Google i GitHub, Turso, walidację danych, PWA oraz responsywny interfejs.
- Klient
- Projekt własny Netova
- Publikacja
- Czas czytania
- 5 min
- Zakres
- Custom

95+/100
Performance
100/100
SEO Score
100/100
Accessibility
AES-256 & WebAuthn
Bezpieczeństwo
Passkeys + 2 OAuth
Logowanie
0.6s
Czas ładowania
Galeria
Spis treści
NetovaTodo to mój własny projekt aplikacji webowej do zarządzania zadaniami. W przeciwieństwie do pozostałych projektów koncepcyjnych nie symuluje współpracy z klientem — powstał jako techniczne laboratorium do sprawdzenia architektury aplikacji, uwierzytelniania i ochrony danych.
Zależało mi na tym, żeby nie kończyć na zwykłym CRUD-zie. Dlatego dodałem kilka warstw, które są typowe dla bardziej dojrzałych aplikacji: szyfrowanie danych, logowanie przez OAuth, Passkeys/WebAuthn, walidację, rate limiting, bazę danych i dopracowane stany interfejsu.
Co chciałem pokazać
- projektowanie aplikacji full-stack w Next.js,
- pracę z bazą Turso / SQLite,
- szyfrowanie danych po stronie serwera,
- OAuth z Google i GitHub,
- logowanie passwordless przez Passkeys/WebAuthn,
- walidację danych i ochronę endpointów,
- responsywny interfejs, dark mode i PWA.
Status projektu: projekt własny. Opis koncentruje się na implementacji i testach technicznych, a nie na wymyślonych wynikach biznesowych.
Jak zbudowałem projekt
Projekt realizowaliśmy jako wewnętrzny showcase — żadnych kompromisów, każda decyzja techniczna uzasadniona.
1. Architektura bezpieczeństwa
Bezpieczeństwo nie jest tu przysłowiowym komentarzem na końcu README. To fundament, od którego zaczyna się każda warstwa aplikacji.
Model zagrożeń, który zaadresowaliśmy:
- Eksfiltracja danych z bazy → szyfrowanie AES-256 po stronie serwera przed zapisem
- Kradzież sesji → JWT z krótkim TTL,
httpOnly,secure,sameSite=strictcookies przez NextAuth - Credential stuffing → brak tradycyjnych haseł; wyłącznie OAuth i Passkeys
- Injection attacks → walidacja każdego inputu przez Zod; baza SQL przez parametryzowane zapytania libSQL
- Rate abuse → in-memory rate limiter per
userId:action— 30 akcji/60 s na użytkownika - CSRF → wbudowana ochrona Next.js Server Actions + CSRF token w formularzach
- Data leakage w URL → IDs zadań jako liczby całkowite, nie UUID; route
/bez identyfikatorów wrażliwych
Dodatkowo wdrożyłem WebAuthn (Passkeys) zintegrowane z NextAuth v5, umożliwiające logowanie przy użyciu Touch ID, Face ID oraz systemowych kluczy sprzętowych bez konieczności wpisywania hasła. Klucze prywatne nigdy nie opuszczają urządzenia użytkownika, dzięki czemu phishing oraz wycieki baz haseł przestają stanowić realny wektor ataku.
Inteligentny Fallback UX
Jednym z problemów WebAuthn jest nieprzyjazne zachowanie przeglądarek wobec nowych użytkowników, którzy nie posiadają jeszcze zarejestrowanego Passkey.
Zamiast wyświetlać techniczne błędy API przeglądarki, aplikacja stosuje Inteligentny Fallback UX.
Jeżeli użytkownik nie posiada jeszcze biometrii:
- aplikacja przechwytuje wyjątek WebAuthn,
- wyświetla estetyczny modal zamiast błędu systemowego,
- informuje o konieczności pierwszego logowania przez Google lub GitHub,
- po zalogowaniu prowadzi użytkownika do ustawień profilu, gdzie może jednym kliknięciem aktywować Passkeys.
Dzięki temu onboarding pozostaje intuicyjny, a jednocześnie cała platforma może przejść na model passwordless-first.
2. Baza danych — Turso (serverless SQLite)
Wybór bazy danych to jedna z ważniejszych decyzji architektonicznych. Wybrałem Turso z czterech powodów:
- Kompatybilność z SQLite — zero vendor lock-in; można zmienić na lokalny plik
.dbw każdej chwili - Serverless z edge replication — latencja poniżej 5 ms z Europy dzięki globalnej sieci edge nodes
- Darmowy tier — 5 GB storage, 500 milionów row reads miesięcznie — wystarczy na produkcję
- Driver
@libsql/client— oficjalny driver z pełnym TypeScript API, typowane zapytania
Struktura tabeli:
┌──────────────────────────────────────────────────┐
│ todos │
├──────────────┬───────────────────────────────────┤
│ id │ INTEGER PRIMARY KEY AUTOINCREMENT │
│ userId │ TEXT NOT NULL │
│ taskName │ TEXT NOT NULL (AES-256) │
│ taskFinished │ NUMERIC DEFAULT 0 │
│ dueDate │ TEXT │
│ createdAt │ NUMERIC DEFAULT CURRENT_TIMESTAMP │
└──────────────┴───────────────────────────────────┘
Pole taskName przechowuje wyłącznie zaszyfrowany ciąg AES-256 — nawet administrator bazy danych bez klucza szyfrowania nie odczyta treści zadań.
Architektura została rozszerzona o tabelę authenticator, przechowującą publiczne klucze WebAuthn. Relacja wykorzystuje kaskadowe usuwanie (ON DELETE CASCADE), dzięki czemu usunięcie konta automatycznie czyści również wszystkie zarejestrowane urządzenia biometryczne, bez pozostawiania osieroconych rekordów.
W celu minimalizacji liczby odczytywanych danych zastosowaliśmy również ultra-lekkie zapytanie server-side:
SELECT 1
FROM authenticator
WHERE userId = ?
LIMIT 1;
Dzięki pobieraniu wyłącznie informacji o istnieniu rekordu zamiast całego obiektu aplikacja znacząco ogranicza transfer danych, zużycie pamięci RAM oraz obciążenie bazy przy każdym renderze interfejsu.
3. Autoryzacja — NextAuth v5
Wybrałem NextAuth v5 (next-auth@5.0.0-beta) — najnowszą generację biblioteki, zoptymalizowaną dla Next.js App Router.
Konfiguracja:
- Strategia JWT — sesje bezstanowe; serwer nie przechowuje tokenów, JWT odnawiany przy każdym żądaniu
- Dostawcy: Google OAuth 2.0 + GitHub OAuth
- Passkeys (WebAuthn) — logowanie biometryczne przez Touch ID, Face ID i Windows Hello
- Stable User ID — generujemy stabilne ID z
account.providerAccountId, nie ztoken.sub; to kluczowe dla integralności danych między loginami - Middleware ochrony tras —
middleware.tssprawdza sesję przed renderem strony; unauthenticated redirect do/login - Potwierdzenie wylogowania — modal z Framer Motion; wylogowanie wymaga potwierdzenia i zatrzymuje scroll strony (bez migotania layout shift)
4. Rozwój
Stack technologiczny:
- Next.js 16 (App Router) — Server Actions jako API; zero osobnego backendu, zero Express; wszystkie mutacje danych przez
"use server"funkcje - React 19 — concurrent features, Actions w formularzach,
useOptimisticdla natychmiastowego UI feedback - Tailwind CSS 4 — utility-first; zero zbędnych klas; idealne Core Web Vitals
- TypeScript 5 — pełne typowanie end-to-end; błędy wychwytywane w IDE, nie na produkcji
- Framer Motion 12 — animacje modala wylogowania (
AnimatePresence+ spring), płynne wejście listy zadań - Zod 4 — walidacja po stronie serwera każdego inputu
- date-fns
- sonner
- use-debounce
- react-icons
- next-themes
- Vercel
5. Architektura komponentów
| Komponent | Odpowiedzialność |
|---|---|
| Navbar | Awatar, imię użytkownika, przycisk wylogowania; fixed top z glassmorphism |
| Greeting | Dynamiczny pozdrowienie zależne od pory dnia (dzień dobry / dobry wieczór) |
| AddTodo | Formularz dodawania z datą, walidacją i debounce; Server Action pod spodem |
| TodoList | Wirtualizowana lista; animacje wejścia Framer Motion; pusta skrzynka state |
| TodoItem | Checkbox, tekst (odszyfrowany), data, usuwanie; optymistyczny update UI |
| Search |
Debounced search przez use-debounce; filtruje po stronie
klienta
|
| LogoutModal | AnimatePresence modal; blokuje scroll bez scroll lock race condition |
| ThemeToggle | next-themes; sun/moon icon; zero FOUC dzięki suppressHydrationWarning |
6. Bezpieczeństwo — szczegóły implementacji
Szyfrowanie AES-256:
Przepływ danych:
User input → Zod validation → AES-256 encrypt (server) → Turso DB
Turso DB → fetch → AES-256 decrypt (server) → React component
Klucz szyfrowania przechowywany wyłącznie w process.env.ENCRYPTION_KEY — nigdy nie trafia do klienta.
Dodatkowo logowanie biometryczne wykorzystuje standard FIDO2/WebAuthn, w którym prywatny klucz kryptograficzny pozostaje na urządzeniu użytkownika, a serwer przechowuje jedynie klucz publiczny wykorzystywany podczas procesu uwierzytelniania.
Architektura wdrożenia
GitHub → Vercel CI/CD → Edge Network
↓
Next.js 16 (Vercel)
↓
Turso (edge SQLite)
Zero infra do zarządzania. Zero cold startów na Vercel Edge. Automatyczny SSL. Deploy przy każdym git push main.
NetovaTodo ma pokazać, że potrafię wyjść poza statyczny frontend i zbudować aplikację z logiką serwerową, bazą danych, uwierzytelnianiem i ochroną danych.
| Obszar | Implementacja / wynik testu |
|---|---|
| Lighthouse Performance | 95+/100 |
| Lighthouse SEO | 100/100 |
| Lighthouse Accessibility | 100/100 |
| LCP | 0,6 s |
| Szyfrowanie danych | AES-256 po stronie serwera |
| Uwierzytelnianie | Passkeys/WebAuthn + Google OAuth + GitHub OAuth |
| Baza danych | Turso / SQLite |
| Walidacja | Zod |
| Rate limiting | 30 żądań/min na użytkownika |
Co wyróżnia ten projekt
To nie jest próba udowodnienia, że stworzyłem ogromny SaaS. To demonstracja konkretnych kompetencji: potrafię połączyć frontend, backend, bazę danych, uwierzytelnianie i warstwę bezpieczeństwa w jeden działający produkt.
Najważniejsze elementy to szyfrowanie danych przed zapisem do bazy, passwordless authentication, obsługa kilku metod logowania, typowanie i walidacja danych oraz architektura gotowa do dalszego rozwijania.
Potrzebujesz aplikacji webowej z podobnym zapleczem? Porozmawiajmy o zakresie i architekturze projektu.