Pomiń do treści
Netova
← Wszystkie realizacje

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
NetovaTodo – projekt własny aplikacji z AES-256, OAuth i Passkeys

95+/100

Performance

100/100

SEO Score

100/100

Accessibility

AES-256 & WebAuthn

Bezpieczeństwo

Passkeys + 2 OAuth

Logowanie

0.6s

Czas ładowania

Galeria

Widok główny – lista zadań z dark mode
Ekran logowania – biometryczne Passkeys i OAuth
Web Vitals NetovaTodo – mobile
Web Vitals NetovaTodo – desktop
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=strict cookies 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 .db w 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 z token.sub; to kluczowe dla integralności danych między loginami
  • Middleware ochrony tras — middleware.ts sprawdza 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, useOptimistic dla 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.

Zobacz projekt na żywo ↗

Inne wdrożenia

Powiązane realizacje

Porozmawiajmy o Twoim projekcie

Opisz cel, a wrócimy z konkretnym kierunkiem działania.

Umów konsultację