---
title: "Архітектура Аутентифікації"
description: "Auth.js v5 багатопровайдерна аутентифікація — Google OAuth, Apple Sign-In, Magic Links, Крипто-гаманці, з PostgreSQL або Firebase бекендом."
locale: "uk"
---
# Архітектура Аутентифікації

> **Info**
> Ring Platform використовує **Auth.js v5** (NextAuth) з JWT стратегією сесій. Адаптер (PostgreSQL або Firebase) обирається за `DB_BACKEND_MODE`.
>   Використовуйте вкладки **Засновник** / **Розробник** у бічній панелі документації для фільтрації за аудиторією.

## Провайдери

| Провайдер | Потік | Auth.js провайдер |
|-----------|------|-------------------|
| **Google OAuth** | Повний OAuth 2.0 redirect + Google Identity Services (GIS) One Tap | `GoogleProvider` + `CredentialsProvider("google-one-tap")` |
| **Apple Sign-In** | OAuth redirect, нативний iOS/macOS | `AppleProvider` |
| **Magic Links** | Email через Ring Mailer, 24-годинний термін дії токену | `Resend` |
| **Крипто-гаманець** | Nonce-підпис верифікація (MetaMask, WalletConnect) | `CredentialsProvider("crypto-wallet")` |

Конфігурація знаходиться в `auth.ts` та `auth.config.ts` у корені проєкту. Auth.js v5 розділяє edge-безпечну конфігурацію (`auth.config.ts` — без провайдерів, мінімальні callbacks) від повної серверної конфігурації (`auth.ts` — всі провайдери, адаптери бази даних).

### For founders

## Як працює аутентифікація

Auth.js v5 керує всім потоком аутентифікації — платформа не використовує Firebase Auth безпосередньо. Firebase Admin SDK використовується лише для **серверної верифікації токенів** та **пошуку документів користувачів** у режимі `firebase-full`.

**Потік сесії:**
1. Користувач входить через Google, Apple, email посилання або крипто-гаманець
2. Auth.js v5 обробляє OAuth обмін токенами та верифікацію
3. JWT сесія створюється на сервері, зберігається в HTTP-only cookie
4. PostgreSQL адаптер зберігає записи користувача/акаунту/сесії коли `DB_BACKEND_MODE` базується на PostgreSQL
5. Firebase адаптер зберігає у Firestore коли `DB_BACKEND_MODE=firebase-full`

**Ключові дизайн-рішення:**
- JWT стратегія (без зберігання сесій в БД) для edge-сумісного розгортання
- 30-денний максимальний вік сесії з 24-годинним вікном оновлення
- Ідентичність завжди є платформним UUID (`users.id`), ніколи Google `sub` або Apple `sub`
- Зв'язування акаунтів через email (однаковий email = той самий користувач)

### For developers

## Структура файлів Auth.js v5

```
auth.config.ts          — Edge-сумісна конфігурація (порожні провайдери, authorized callback, redirects)
auth.ts                 — Повна серверна конфігурація (всі провайдери, адаптер БД, JWT/сесійні callbacks)
lib/auth-adapter-singleton.ts  — Кешований адаптер: PostgreSQLAdapter або FirestoreAdapter
lib/auth/postgres-adapter.ts   — Спеціалізований PostgreSQL адаптер для Auth.js v5
lib/firebase-admin.server.ts   — Firebase Admin SDK екземпляр (getAdminAuth, getAdminDb)
app/api/auth/[...nextauth]/route.ts  — Auth.js API route handler
```

## Конфігурація провайдерів

### Google OAuth (два режими)

**Традиційний OAuth** (redirect потік):

{`GoogleProvider({
  allowDangerousEmailAccountLinking: true,
  checks: ["pkce", "state"],
  wellKnown: "https://accounts.google.com/.well-known/openid-configuration",
})`}

**Google Identity Services (GIS) One Tap** (клієнтське спливаюче вікно):

{`CredentialsProvider({
  id: 'google-one-tap',
  name: 'Google One Tap',
  credentials: { credential: { type: 'text' } },
  async authorize(credentials) {
    // JWT верифікується на сервері в signIn callback
    return { id: 'gis-jwt-pending', email: credentials.credential }
  },
})`}

GIS JWT верифікується на сервері в `signIn` callback за допомогою `google-auth-library`:

{`const ticket = await googleAuthClient.verifyIdToken({
  idToken: credential,
  audience: getGoogleIdTokenAudiences(),
})`}

### Apple Sign-In

{`AppleProvider({
  allowDangerousEmailAccountLinking: true,
})`}

### Magic Links (Resend)

Потребує SMTP_* або EMAIL_MODE=ethereal змінну середовища та активний адаптер бази даних (для зберігання токенів):

{`/* Ring Mailer Credentials — see auth.ts */`}

24-годинний термін дії токену (maxAge в Resend провайдері), одноразове використання, автоматична інвалідація.

### Крипто-гаманець

Nonce-підпис верифікація через Viem. Підтримує Ethereum, Polygon, Arbitrum, Optimism та Base:

{`CredentialsProvider({
  id: "crypto-wallet",
  credentials: {
    walletAddress: { label: "Wallet Address", type: "text" },
    signedNonce: { label: "Signed Nonce", type: "text" },
  },
  async authorize(credentials) {
    // 1. Пошук користувача за адресою гаманця
    // 2. Верифікація підпису nonce через verifyWalletNonceSignature()
    // 3. Очищення nonce, повернення об'єкту користувача
    // 4. JWT callback створює сесію
  },
})`}

## Вибір адаптера

Адаптер визначається `DB_BACKEND_MODE`:

| Режим | Адаптер | Джерело |
|-------|---------|---------|
| `k8s-postgres-fcm` | `PostgreSQLAdapter` | `lib/auth/postgres-adapter.ts` |
| `firebase-full` | `FirestoreAdapter` з `@auth/firebase-adapter` | через `getAdminDb()` |
| `supabase-fcm` | `PostgreSQLAdapter` | Той самий PostgreSQL шлях |

{`export function getAuthAdapter() {
  const { shouldUseFirebaseForDatabase } = require('./database/backend-mode-config')
  const useFirebase = shouldUseFirebaseForDatabase()

  if (!useFirebase) {
    return PostgreSQLAdapter()
  }
  const { getAdminDb } = require("@/lib/firebase-admin.server")
  const adminDb = getAdminDb()
  return FirestoreAdapter(adminDb)
}`}

## Інтеграція Firebase Admin SDK

Firebase Admin SDK використовується у двох контекстах:
1. **Auth адаптер** (тільки `firebase-full` режим): `FirestoreAdapter` читає/записує документи користувачів
2. **Крипто-гаманець аутентифікація** (`firebase-full` режим): `getAdminDb().collection("users")` для пошуку nonce

У режимах `k8s-postgres-fcm` та `supabase-fcm`, `getAdminDb()` повертає **mock Firestore** — жодної реальної ініціалізації Firebase не відбувається. FCM push сповіщення все ще використовують Firebase Admin через `firebase-admin.server.ts` (окремо від auth шляху).

## Використання на сервері

{`import { auth } from '@/auth'

export default async function ProfilePage() {
  const session = await auth()
  if (!session) return Будь ласка, увійдіть
  return Вітаємо, {session.user.name}!
}`}

## Використання на клієнті

{`'use client'
import { useSession } from 'next-auth/react'

export default function UserProfile() {
  const { data: session, status } = useSession()
  if (status === 'loading') return Завантаження...
  if (!session) return Не аутентифіковано
  return Користувач: {session.user.email}
}`}

## Змінні середовища

```bash
# Auth.js core
AUTH_SECRET=your_auth_secret
AUTH_TRUST_HOST=true

# Google OAuth
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret

# Apple Sign-In
AUTH_APPLE_ID=your_apple_client_id
AUTH_APPLE_SECRET=your_apple_private_key

# Magic Links
# Ring Mailer: EMAIL_MODE=ethereal or SMTP_HOST / SMTP_USER / SMTP_PASSWORD
# OTP_HMAC_SECRET=

# Firebase (тільки для firebase-full режиму)
AUTH_FIREBASE_PROJECT_ID=your_firebase_project_id
AUTH_FIREBASE_CLIENT_EMAIL=your_firebase_client_email
AUTH_FIREBASE_PRIVATE_KEY=your_firebase_private_key

# WalletConnect
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_project_id
```

## Пов'язана документація
