Documentation

    Концепції, цінність і типові сценарії

    Ласкаво просимо до Ring
    Швидкий довідник
    Початок роботи
    Передумови
    Встановлення
    Валідація першого успіху
    Наступні кроки
    Функції
    Магазин
    Склад і залишки
    Керування вендорами
    Комісії та розрахунки
    SubscriptionConductor
    PaymentConductor
    Інтеграція платежів
    Інтеграція WayForPay
    Web3 Гаманець
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкінга Токенів
    Сутності
    Можливості
    Повідомлення
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безпека та відповідність
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Білий лейбл
    Інтеграція Web3
    Реальні приклади
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Web3
    Token launch jurisdictions
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та аналітика
    Оптимізація продуктивності
    Резервне копіювання та відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Архітектура PaymentConductor
    Розробка
    Ring MCP Server

    Швидкий вхід (CTO · аудитори · агенти)

    Вітаємо — місія та аудиторії
    Швидкий довідник
    Початок роботи
    Архітектура та Auth.js
    Режими бекенду та БД (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Деплой (Docker · k8s)
    Безпека та відповідність
    ringdom.org — база LegioX
    Код — ліцензія MIT (GitHub)

    Documentation

    Концепції, цінність і типові сценарії

    Ласкаво просимо до Ring
    Швидкий довідник
    Початок роботи
    Передумови
    Встановлення
    Валідація першого успіху
    Наступні кроки
    Функції
    Магазин
    Склад і залишки
    Керування вендорами
    Комісії та розрахунки
    SubscriptionConductor
    PaymentConductor
    Інтеграція платежів
    Інтеграція WayForPay
    Web3 Гаманець
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкінга Токенів
    Сутності
    Можливості
    Повідомлення
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безпека та відповідність
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Білий лейбл
    Інтеграція Web3
    Реальні приклади
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Web3
    Token launch jurisdictions
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та аналітика
    Оптимізація продуктивності
    Резервне копіювання та відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Архітектура PaymentConductor
    Розробка
    Ring MCP Server

    Швидкий вхід (CTO · аудитори · агенти)

    Вітаємо — місія та аудиторії
    Швидкий довідник
    Початок роботи
    Архітектура та Auth.js
    Режими бекенду та БД (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Деплой (Docker · k8s)
    Безпека та відповідність
    ringdom.org — база LegioX
    Код — ліцензія MIT (GitHub)

    Documentation

    Концепції, цінність і типові сценарії

    Ласкаво просимо до Ring
    Швидкий довідник
    Початок роботи
    Передумови
    Встановлення
    Валідація першого успіху
    Наступні кроки
    Функції
    Магазин
    Склад і залишки
    Керування вендорами
    Комісії та розрахунки
    SubscriptionConductor
    PaymentConductor
    Інтеграція платежів
    Інтеграція WayForPay
    Web3 Гаманець
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкінга Токенів
    Сутності
    Можливості
    Повідомлення
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безпека та відповідність
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Білий лейбл
    Інтеграція Web3
    Реальні приклади
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Web3
    Token launch jurisdictions
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та аналітика
    Оптимізація продуктивності
    Резервне копіювання та відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Архітектура PaymentConductor
    Розробка
    Ring MCP Server

    Швидкий вхід (CTO · аудитори · агенти)

    Вітаємо — місія та аудиторії
    Швидкий довідник
    Початок роботи
    Архітектура та Auth.js
    Режими бекенду та БД (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Деплой (Docker · k8s)
    Безпека та відповідність
    ringdom.org — база LegioX
    Код — ліцензія MIT (GitHub)
    1. Документація
    2. /Приклади
    3. /Приклади аутентифікації

    Оновлено 28 черв. 2026 р.2 хв прослуховування

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Документація
    2. /Приклади
    3. /Приклади аутентифікації

    Оновлено 28 черв. 2026 р.2 хв прослуховування

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    1. Документація
    2. /Приклади
    3. /Приклади аутентифікації

    Оновлено 28 черв. 2026 р.2 хв прослуховування

    Ring Platform Logo

    Завантаження документації...

    Підготовка контенту платформи Ring

    Приклади аутентифікації

    Повні шаблони реалізації аутентифікації з використанням Auth.js v5 на Ring Platform.

    Див. документ Архітектура аутентифікації для огляду провайдерів та дизайнерських рішень. Повну довідку змінних середовища див. у Змінні середовища.

    Огляд провайдерів

    Ring Platform підтримує п'ять провайдерів аутентифікації, усі керуються через Auth.js v5:

    ПровайдерЩо він дає
    Google OAuthТрадиційне OAuth перенаправлення + Google One Tap (клієнтське GIS вікно з серверною JWT верифікацією)
    Apple Sign-InВхід через OAuth перенаправлення для iOS/macOS
    Ring MailerВхід без пароля через email. Термін дії токена 24 години, одноразове використання. Потребує SMTP_* або EMAIL_MODE=ethereal
    Crypto WalletВерифікація підпису через nonce за допомогою Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTТокени machine-to-machine для аутентифікації WebSocket та MCP шлюзу

    Стратегія сесії: JWT (без серверного сховища сесій). Максимальний вік 30 днів, вікно оновлення 24 години.

    Адаптер бази даних (PostgreSQL або Firebase) обирається автоматично за DB_BACKEND_MODE. Детальніше див. Режими бекенду та бази даних.

    Конфігурація сервера Auth.js v5

    Стратегія облікових даних Firebase

    Firebase Admin SDK спочатку використовує Application Default Credentials (ADC). Резервний варіант cert() з явними обліковими даними сервісного акаунта використовується лише за наявності AUTH_FIREBASE_CLIENT_EMAIL та AUTH_FIREBASE_PRIVATE_KEY:

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

    Адаптер визначається DB_BACKEND_MODE та ініціалізується через синглтон:

    DB_BACKEND_MODEАдаптер
    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Серверна сесія

    Клієнтська сесія

    Налаштування провайдера сесії

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

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

    Authentication Architecture

    Environment Variables

    Backend Modes

    Приклади аутентифікації

    Повні шаблони реалізації аутентифікації з використанням Auth.js v5 на Ring Platform.

    Див. документ Архітектура аутентифікації для огляду провайдерів та дизайнерських рішень. Повну довідку змінних середовища див. у Змінні середовища.

    Огляд провайдерів

    Ring Platform підтримує п'ять провайдерів аутентифікації, усі керуються через Auth.js v5:

    ПровайдерЩо він дає
    Google OAuthТрадиційне OAuth перенаправлення + Google One Tap (клієнтське GIS вікно з серверною JWT верифікацією)
    Apple Sign-InВхід через OAuth перенаправлення для iOS/macOS
    Ring MailerВхід без пароля через email. Термін дії токена 24 години, одноразове використання. Потребує SMTP_* або EMAIL_MODE=ethereal
    Crypto WalletВерифікація підпису через nonce за допомогою Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTТокени machine-to-machine для аутентифікації WebSocket та MCP шлюзу

    Стратегія сесії: JWT (без серверного сховища сесій). Максимальний вік 30 днів, вікно оновлення 24 години.

    Адаптер бази даних (PostgreSQL або Firebase) обирається автоматично за DB_BACKEND_MODE. Детальніше див. Режими бекенду та бази даних.

    Конфігурація сервера Auth.js v5

    Стратегія облікових даних Firebase

    Firebase Admin SDK спочатку використовує Application Default Credentials (ADC). Резервний варіант cert() з явними обліковими даними сервісного акаунта використовується лише за наявності AUTH_FIREBASE_CLIENT_EMAIL та AUTH_FIREBASE_PRIVATE_KEY:

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

    Адаптер визначається DB_BACKEND_MODE та ініціалізується через синглтон:

    DB_BACKEND_MODEАдаптер
    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Серверна сесія

    Клієнтська сесія

    Налаштування провайдера сесії

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

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

    Authentication Architecture

    Environment Variables

    Backend Modes

    Приклади аутентифікації

    Повні шаблони реалізації аутентифікації з використанням Auth.js v5 на Ring Platform.

    Див. документ Архітектура аутентифікації для огляду провайдерів та дизайнерських рішень. Повну довідку змінних середовища див. у Змінні середовища.

    Огляд провайдерів

    Ring Platform підтримує п'ять провайдерів аутентифікації, усі керуються через Auth.js v5:

    ПровайдерЩо він дає
    Google OAuthТрадиційне OAuth перенаправлення + Google One Tap (клієнтське GIS вікно з серверною JWT верифікацією)
    Apple Sign-InВхід через OAuth перенаправлення для iOS/macOS
    Ring MailerВхід без пароля через email. Термін дії токена 24 години, одноразове використання. Потребує SMTP_* або EMAIL_MODE=ethereal
    Crypto WalletВерифікація підпису через nonce за допомогою Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTТокени machine-to-machine для аутентифікації WebSocket та MCP шлюзу

    Стратегія сесії: JWT (без серверного сховища сесій). Максимальний вік 30 днів, вікно оновлення 24 години.

    Адаптер бази даних (PostgreSQL або Firebase) обирається автоматично за DB_BACKEND_MODE. Детальніше див. Режими бекенду та бази даних.

    Конфігурація сервера Auth.js v5

    Стратегія облікових даних Firebase

    Firebase Admin SDK спочатку використовує Application Default Credentials (ADC). Резервний варіант cert() з явними обліковими даними сервісного акаунта використовується лише за наявності AUTH_FIREBASE_CLIENT_EMAIL та AUTH_FIREBASE_PRIVATE_KEY:

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

    Адаптер визначається DB_BACKEND_MODE та ініціалізується через синглтон:

    DB_BACKEND_MODEАдаптер
    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Серверна сесія

    Клієнтська сесія

    Налаштування провайдера сесії

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

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

    Authentication Architecture

    Environment Variables

    Backend Modes

    Auth.js v5 автоматично зчитує AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_APPLE_ID, AUTH_APPLE_SECRET (Ring Mailer SMTP) зі змінних середовища — не потребує ручного налаштування clientId / clientSecret у конструкторі провайдера.

    Захист маршрутів через middleware

    typescript
    
    import NextAuth from "next-auth"
    import { getAuthAdapter } from "@/lib/auth-adapter-singleton"
    import authConfig from "./auth.config"
    import GoogleProvider from "next-auth/providers/google"
    import AppleProvider from "next-auth/providers/apple"
    import CredentialsProvider from "next-auth/providers/credentials"
    
    const authAdapter = getAuthAdapter()
    const hasAdapter = !!authAdapter
    export const { handlers, signIn, signOut, auth } = NextAuth({
      ...authConfig,
      ...(hasAdapter && { adapter: authAdapter }),
      session: {
        strategy: "jwt",
        maxAge: 30 * 24 * 60 * 60,
        updateAge: 24 * 60 * 60,
      },
      trustHost: true,
      providers: [
        // Ring Mailer: email-otp / email-magic Credentials — see EN /docs/features/ring-mailer
    
        // Google OAuth
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        // Google One Tap (client-side GIS popup)
        CredentialsProvider({
          id: "google-one-tap",
          name: "Google One Tap",
          credentials: { credential: { type: "text" } },
          async authorize(credentials) {
            if (!credentials?.credential) return null
            return { id: "gis-jwt-pending", email: credentials.credential as string }
          },
        }),
    
        // Apple Sign-In
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        // Crypto Wallet
        CredentialsProvider({
          id: "crypto-wallet",
          credentials: {
            walletAddress: { label: "Wallet Address", type: "text" },
            signedNonce: { label: "Signed Nonce", type: "text" },
          },
          async authorize(credentials) {
            if (!credentials?.walletAddress || !credentials?.signedNonce) return null
            // Nonce signature verification via Viem
            // Returns user object on success, null on failure
          },
        }),
      ],
      callbacks: {
        async jwt({ token, user, account, trigger }) {
          // Fetch fresh user data from database
          // Apply user row to JWT via applyUserRowToJwt()
          // Generate internal JWT for WebSocket auth
          return token
        },
        async session({ session, token }) {
          session.user.id = token.userId as string
          session.user.role = token.role as string
          session.user.isVerified = token.isVerified as boolean
          session.accessToken = token.accessToken as string
          return session
        },
        async signIn({ user, account, profile }) {
          // Google One Tap: verify JWT via google-auth-library
          // Check account status, block suspended users
          return true
        },
      },
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    // ADC-first (працює на Cloud Run, GKE, Cloud Functions без ручного налаштування)
    // cert() резервний варіант для локальної розробки / CI, коли встановлено змінні середовища:
    adminApp = initializeApp({
      credential: cert({
        projectId: process.env.AUTH_FIREBASE_PROJECT_ID,
        clientEmail: process.env.AUTH_FIREBASE_CLIENT_EMAIL,
        privateKey: process.env.AUTH_FIREBASE_PRIVATE_KEY,
      }),
    })
    typescript
    
    import { FirestoreAdapter } from "@auth/firebase-adapter"
    import { PostgreSQLAdapter } from "@/lib/auth/postgres-adapter"
    import { shouldUseFirebaseForDatabase } from "@/lib/database/backend-mode-config"
    
    export function getAuthAdapter() {
      if (shouldUseFirebaseForDatabase()) {
        const { getAdminDb } = require("@/lib/firebase-admin.server")
        return FirestoreAdapter(getAdminDb())
      }
      return PostgreSQLAdapter()
    }
    typescript
    
    import { auth } from "@/auth"
    
    export default async function ProfilePage() {
      const session = await auth()
      if (!session) return <div>Будь ласка, увійдіть</div>
      return <div>Вітаємо, {session.user.name}</div>
    }
    typescript
    
    "use client"
    import { useSession } from "next-auth/react"
    
    export default function UserProfile() {
      const { data: session, status } = useSession()
      if (status === "loading") return <div>Завантаження...</div>
      if (!session) return <div>Не аутентифіковано</div>
      return <div>Користувач: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "next-auth/react"
    
    export function Providers({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }

    Auth.js v5 автоматично зчитує AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_APPLE_ID, AUTH_APPLE_SECRET (Ring Mailer SMTP) зі змінних середовища — не потребує ручного налаштування clientId / clientSecret у конструкторі провайдера.

    Захист маршрутів через middleware

    typescript
    
    import NextAuth from "next-auth"
    import { getAuthAdapter } from "@/lib/auth-adapter-singleton"
    import authConfig from "./auth.config"
    import GoogleProvider from "next-auth/providers/google"
    import AppleProvider from "next-auth/providers/apple"
    import CredentialsProvider from "next-auth/providers/credentials"
    
    const authAdapter = getAuthAdapter()
    const hasAdapter = !!authAdapter
    export const { handlers, signIn, signOut, auth } = NextAuth({
      ...authConfig,
      ...(hasAdapter && { adapter: authAdapter }),
      session: {
        strategy: "jwt",
        maxAge: 30 * 24 * 60 * 60,
        updateAge: 24 * 60 * 60,
      },
      trustHost: true,
      providers: [
        // Ring Mailer: email-otp / email-magic Credentials — see EN /docs/features/ring-mailer
    
        // Google OAuth
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        // Google One Tap (client-side GIS popup)
        CredentialsProvider({
          id: "google-one-tap",
          name: "Google One Tap",
          credentials: { credential: { type: "text" } },
          async authorize(credentials) {
            if (!credentials?.credential) return null
            return { id: "gis-jwt-pending", email: credentials.credential as string }
          },
        }),
    
        // Apple Sign-In
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        // Crypto Wallet
        CredentialsProvider({
          id: "crypto-wallet",
          credentials: {
            walletAddress: { label: "Wallet Address", type: "text" },
            signedNonce: { label: "Signed Nonce", type: "text" },
          },
          async authorize(credentials) {
            if (!credentials?.walletAddress || !credentials?.signedNonce) return null
            // Nonce signature verification via Viem
            // Returns user object on success, null on failure
          },
        }),
      ],
      callbacks: {
        async jwt({ token, user, account, trigger }) {
          // Fetch fresh user data from database
          // Apply user row to JWT via applyUserRowToJwt()
          // Generate internal JWT for WebSocket auth
          return token
        },
        async session({ session, token }) {
          session.user.id = token.userId as string
          session.user.role = token.role as string
          session.user.isVerified = token.isVerified as boolean
          session.accessToken = token.accessToken as string
          return session
        },
        async signIn({ user, account, profile }) {
          // Google One Tap: verify JWT via google-auth-library
          // Check account status, block suspended users
          return true
        },
      },
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    // ADC-first (працює на Cloud Run, GKE, Cloud Functions без ручного налаштування)
    // cert() резервний варіант для локальної розробки / CI, коли встановлено змінні середовища:
    adminApp = initializeApp({
      credential: cert({
        projectId: process.env.AUTH_FIREBASE_PROJECT_ID,
        clientEmail: process.env.AUTH_FIREBASE_CLIENT_EMAIL,
        privateKey: process.env.AUTH_FIREBASE_PRIVATE_KEY,
      }),
    })
    typescript
    
    import { FirestoreAdapter } from "@auth/firebase-adapter"
    import { PostgreSQLAdapter } from "@/lib/auth/postgres-adapter"
    import { shouldUseFirebaseForDatabase } from "@/lib/database/backend-mode-config"
    
    export function getAuthAdapter() {
      if (shouldUseFirebaseForDatabase()) {
        const { getAdminDb } = require("@/lib/firebase-admin.server")
        return FirestoreAdapter(getAdminDb())
      }
      return PostgreSQLAdapter()
    }
    typescript
    
    import { auth } from "@/auth"
    
    export default async function ProfilePage() {
      const session = await auth()
      if (!session) return <div>Будь ласка, увійдіть</div>
      return <div>Вітаємо, {session.user.name}</div>
    }
    typescript
    
    "use client"
    import { useSession } from "next-auth/react"
    
    export default function UserProfile() {
      const { data: session, status } = useSession()
      if (status === "loading") return <div>Завантаження...</div>
      if (!session) return <div>Не аутентифіковано</div>
      return <div>Користувач: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "next-auth/react"
    
    export function Providers({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }

    Auth.js v5 автоматично зчитує AUTH_GOOGLE_ID, AUTH_GOOGLE_SECRET, AUTH_APPLE_ID, AUTH_APPLE_SECRET (Ring Mailer SMTP) зі змінних середовища — не потребує ручного налаштування clientId / clientSecret у конструкторі провайдера.

    Захист маршрутів через middleware

    typescript
    
    import NextAuth from "next-auth"
    import { getAuthAdapter } from "@/lib/auth-adapter-singleton"
    import authConfig from "./auth.config"
    import GoogleProvider from "next-auth/providers/google"
    import AppleProvider from "next-auth/providers/apple"
    import CredentialsProvider from "next-auth/providers/credentials"
    
    const authAdapter = getAuthAdapter()
    const hasAdapter = !!authAdapter
    export const { handlers, signIn, signOut, auth } = NextAuth({
      ...authConfig,
      ...(hasAdapter && { adapter: authAdapter }),
      session: {
        strategy: "jwt",
        maxAge: 30 * 24 * 60 * 60,
        updateAge: 24 * 60 * 60,
      },
      trustHost: true,
      providers: [
        // Ring Mailer: email-otp / email-magic Credentials — see EN /docs/features/ring-mailer
    
        // Google OAuth
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        // Google One Tap (client-side GIS popup)
        CredentialsProvider({
          id: "google-one-tap",
          name: "Google One Tap",
          credentials: { credential: { type: "text" } },
          async authorize(credentials) {
            if (!credentials?.credential) return null
            return { id: "gis-jwt-pending", email: credentials.credential as string }
          },
        }),
    
        // Apple Sign-In
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        // Crypto Wallet
        CredentialsProvider({
          id: "crypto-wallet",
          credentials: {
            walletAddress: { label: "Wallet Address", type: "text" },
            signedNonce: { label: "Signed Nonce", type: "text" },
          },
          async authorize(credentials) {
            if (!credentials?.walletAddress || !credentials?.signedNonce) return null
            // Nonce signature verification via Viem
            // Returns user object on success, null on failure
          },
        }),
      ],
      callbacks: {
        async jwt({ token, user, account, trigger }) {
          // Fetch fresh user data from database
          // Apply user row to JWT via applyUserRowToJwt()
          // Generate internal JWT for WebSocket auth
          return token
        },
        async session({ session, token }) {
          session.user.id = token.userId as string
          session.user.role = token.role as string
          session.user.isVerified = token.isVerified as boolean
          session.accessToken = token.accessToken as string
          return session
        },
        async signIn({ user, account, profile }) {
          // Google One Tap: verify JWT via google-auth-library
          // Check account status, block suspended users
          return true
        },
      },
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    // ADC-first (працює на Cloud Run, GKE, Cloud Functions без ручного налаштування)
    // cert() резервний варіант для локальної розробки / CI, коли встановлено змінні середовища:
    adminApp = initializeApp({
      credential: cert({
        projectId: process.env.AUTH_FIREBASE_PROJECT_ID,
        clientEmail: process.env.AUTH_FIREBASE_CLIENT_EMAIL,
        privateKey: process.env.AUTH_FIREBASE_PRIVATE_KEY,
      }),
    })
    typescript
    
    import { FirestoreAdapter } from "@auth/firebase-adapter"
    import { PostgreSQLAdapter } from "@/lib/auth/postgres-adapter"
    import { shouldUseFirebaseForDatabase } from "@/lib/database/backend-mode-config"
    
    export function getAuthAdapter() {
      if (shouldUseFirebaseForDatabase()) {
        const { getAdminDb } = require("@/lib/firebase-admin.server")
        return FirestoreAdapter(getAdminDb())
      }
      return PostgreSQLAdapter()
    }
    typescript
    
    import { auth } from "@/auth"
    
    export default async function ProfilePage() {
      const session = await auth()
      if (!session) return <div>Будь ласка, увійдіть</div>
      return <div>Вітаємо, {session.user.name}</div>
    }
    typescript
    
    "use client"
    import { useSession } from "next-auth/react"
    
    export default function UserProfile() {
      const { data: session, status } = useSession()
      if (status === "loading") return <div>Завантаження...</div>
      if (!session) return <div>Не аутентифіковано</div>
      return <div>Користувач: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "next-auth/react"
    
    export function Providers({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }
    bash
    
    # Auth.js core
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    # Google OAuth (Auth.js auto-prefix)
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    # Apple Sign-In (Auth.js auto-prefix)
    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 Admin (для режиму 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
    typescript
    
    import { auth } from "@/auth"
    import { NextResponse } from "next/server"
    
    export default auth((req) => {
      const { pathname } = req.nextUrl
      const session = req.auth
    
      if (pathname.startsWith("/auth") || pathname === "/") {
        return NextResponse.next()
      }
      if (!session && pathname.startsWith("/dashboard")) {
        return NextResponse.redirect(new URL("/login", req.url))
      }
      if (pathname.startsWith("/admin") && session?.user?.role !== "ADMIN") {
        return NextResponse.redirect(new URL("/403", req.url))
      }
      return NextResponse.next()
    })
    
    export const config = {
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
    }
    bash
    
    # Auth.js core
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    # Google OAuth (Auth.js auto-prefix)
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    # Apple Sign-In (Auth.js auto-prefix)
    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 Admin (для режиму 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
    typescript
    
    import { auth } from "@/auth"
    import { NextResponse } from "next/server"
    
    export default auth((req) => {
      const { pathname } = req.nextUrl
      const session = req.auth
    
      if (pathname.startsWith("/auth") || pathname === "/") {
        return NextResponse.next()
      }
      if (!session && pathname.startsWith("/dashboard")) {
        return NextResponse.redirect(new URL("/login", req.url))
      }
      if (pathname.startsWith("/admin") && session?.user?.role !== "ADMIN") {
        return NextResponse.redirect(new URL("/403", req.url))
      }
      return NextResponse.next()
    })
    
    export const config = {
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
    }
    bash
    
    # Auth.js core
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    # Google OAuth (Auth.js auto-prefix)
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    # Apple Sign-In (Auth.js auto-prefix)
    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 Admin (для режиму 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
    typescript
    
    import { auth } from "@/auth"
    import { NextResponse } from "next/server"
    
    export default auth((req) => {
      const { pathname } = req.nextUrl
      const session = req.auth
    
      if (pathname.startsWith("/auth") || pathname === "/") {
        return NextResponse.next()
      }
      if (!session && pathname.startsWith("/dashboard")) {
        return NextResponse.redirect(new URL("/login", req.url))
      }
      if (pathname.startsWith("/admin") && session?.user?.role !== "ADMIN") {
        return NextResponse.redirect(new URL("/403", req.url))
      }
      return NextResponse.next()
    })
    
    export const config = {
      matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
    }