МожливостіСутності
Документація
    Ring Platform

    Децентралізоване самобудівне майбутнє

    Увійти
    Сутності
    Можливості
    Магазин
    Документація
    Концепції
    RING ЕкономікаSonoratek LLCГлобальний впливAI зустрічає Web3
    Розпочати
    Швидкий стартКалькуляторДорожня карта
    Конфіденційність|Контакти
    v1.104.17|Sonoratek LLC

    Documentation

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

    Ласкаво просимо до Ring
    Швидкий довідник
    Початок роботи
    Передумови
    Встановлення
    Валідація першого успіху
    Наступні кроки
    Функції
    Магазин
    Склад і залишки
    Керування вендорами
    Комісії та розрахунки
    SubscriptionConductor
    PaymentConductor
    Ring Oracle
    Інтеграція платежів
    Публічні пули та DAO-банки
    Інтеграція WayForPay
    Web3 Гаманець
    WalletConductor
    Партнерська та реферальна підтримка
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Система Стейкінга Токенів
    Лабораторія проєкту власника (Owner Project Lab)
    Сутності
    Можливості
    Повідомлення
    Завдання Ring
    WebRTC-дзвінки та STUNner TURN
    Ігри з друзями
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Сторінки публічного профілю
    Віджети профілю облікового запису
    Ring File Cabinet
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Ring Mailer і RingdomX Mail
    Протокол Tunnel
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Безпека та відповідність
    Консоль адміністратора
    Адмінська Wiki
    Керування через Telegram
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Базове налаштування
    Білий лейбл
    Користувацьке брендування
    Інтеграція Web3
    Реальні приклади
    Розширені функції
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Вертикальні пресети (SSOT)
    Ringization playbook
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Конфігурація
    Публічні змінні середовища
    Секрети Order Lab
    Project ID WalletConnect (Reown Cloud)
    Підтримувані сервіси
    Вікі NODUS (знання проєкту)
    Плейбук конфігурації
    Web3
    Token launch jurisdictions
    Гаманець
    Поради з безпеки гаманця
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    RingFileBase (API об’єктного сховища)
    Ring CDN (edge RingFileBase)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та Аналітика
    Оптимізація продуктивності
    Резервне копіювання і відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Синхронізація discovery після мутацій
    Архітектура PaymentConductor
    Архітектура WalletConductor
    Бекенд Сервіси
    Інтеграція Firebase
    Розробка
    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
    Ring Oracle
    Інтеграція платежів
    Публічні пули та DAO-банки
    Інтеграція WayForPay
    Web3 Гаманець
    WalletConductor
    Партнерська та реферальна підтримка
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Система Стейкінга Токенів
    Лабораторія проєкту власника (Owner Project Lab)
    Сутності
    Можливості
    Повідомлення
    Завдання Ring
    WebRTC-дзвінки та STUNner TURN
    Ігри з друзями
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Сторінки публічного профілю
    Віджети профілю облікового запису
    Ring File Cabinet
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Ring Mailer і RingdomX Mail
    Протокол Tunnel
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Безпека та відповідність
    Консоль адміністратора
    Адмінська Wiki
    Керування через Telegram
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Базове налаштування
    Білий лейбл
    Користувацьке брендування
    Інтеграція Web3
    Реальні приклади
    Розширені функції
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Вертикальні пресети (SSOT)
    Ringization playbook
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Конфігурація
    Публічні змінні середовища
    Секрети Order Lab
    Project ID WalletConnect (Reown Cloud)
    Підтримувані сервіси
    Вікі NODUS (знання проєкту)
    Плейбук конфігурації
    Web3
    Token launch jurisdictions
    Гаманець
    Поради з безпеки гаманця
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    RingFileBase (API об’єктного сховища)
    Ring CDN (edge RingFileBase)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та Аналітика
    Оптимізація продуктивності
    Резервне копіювання і відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Синхронізація discovery після мутацій
    Архітектура PaymentConductor
    Архітектура WalletConductor
    Бекенд Сервіси
    Інтеграція Firebase
    Розробка
    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
    Ring Oracle
    Інтеграція платежів
    Публічні пули та DAO-банки
    Інтеграція WayForPay
    Web3 Гаманець
    WalletConductor
    Партнерська та реферальна підтримка
    Реферальні коди (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Система Стейкінга Токенів
    Лабораторія проєкту власника (Owner Project Lab)
    Сутності
    Можливості
    Повідомлення
    Завдання Ring
    WebRTC-дзвінки та STUNner TURN
    Ігри з друзями
    Модуль Новин - Цифровий Газетний Досвід
    Блоги учасників
    Сторінки публічного профілю
    Віджети профілю облікового запису
    Ring File Cabinet
    Система Резервування Імен Користувачів
    Науковий редактор
    Notifications
    Push-сповіщення через FCM (Ring)
    Email AI-CRM
    Ring Mailer і RingdomX Mail
    Протокол Tunnel
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Безпека та відповідність
    Консоль адміністратора
    Адмінська Wiki
    Керування через Telegram
    Система локалей
    Мобільний Досвід
    Паттерни Оптимізації Продуктивності
    Приклади
    Швидкий старт
    Базове налаштування
    Білий лейбл
    Користувацьке брендування
    Інтеграція Web3
    Реальні приклади
    Розширені функції
    Кастомізація
    Швидкий старт — ваш перший клон Ring
    Повний посібник налаштування
    Вертикальні пресети (SSOT)
    Ringization playbook
    Брендування
    Теми
    Функції
    Локалізація
    Налаштування токеноміки
    Інтеграція платіжних шлюзів
    Еталонні деплої Ring
    Конфігурація
    Публічні змінні середовища
    Секрети Order Lab
    Project ID WalletConnect (Reown Cloud)
    Підтримувані сервіси
    Вікі NODUS (знання проєкту)
    Плейбук конфігурації
    Web3
    Token launch jurisdictions
    Гаманець
    Поради з безпеки гаманця
    Інтеграції
    Ethereum гаманці (Wagmi v3)
    RingFileBase (API об’єктного сховища)
    Ring CDN (edge RingFileBase)
    Розгортання
    Self-hosted розгортання
    Vercel
    Docker
    Конфігурація Середовища
    Моніторинг та Аналітика
    Оптимізація продуктивності
    Резервне копіювання і відновлення
    Архітектура
    Data Model
    Security
    Real Time
    Синхронізація discovery після мутацій
    Архітектура PaymentConductor
    Архітектура WalletConductor
    Бекенд Сервіси
    Інтеграція Firebase
    Розробка
    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)
    Ring Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

    Ring Logo

    Loading documentation...

    Preparing Ring content

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

    Повні шаблони реалізації аутентифікації з використанням 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).*)"],
    }
    1. Документація
    2. /Приклади
    3. /Приклади аутентифікації

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

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

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

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

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