Documentation

    Концепции, ценность и типичные сценарии клонирования — меньше кода.

    Добро пожаловать в Ring Platform
    Краткий справочник
    С чего начать
    Предварительные требования
    Установка
    Первый успех
    Следующие шаги
    Функции
    Store
    Склад и остатки
    Управление вендорами
    Комиссии и расчёты
    SubscriptionConductor
    PaymentConductor
    Интеграция платежей
    Интеграция WayForPay
    Web3 Кошелек
    Реферальные коды (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкинга Токенов
    Entities
    Opportunities
    Messaging
    Модуль Новостей - Цифровой Газетный Опыт
    Блоги участников
    Система Резервирования Имён Пользователей
    Научный редактор
    Notifications
    Push-уведомления через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безопасность и соответствие
    Система локалей
    Мобильный Опыт
    Паттерны Оптимизации Производительности
    Примеры
    Quick Start
    White Label
    Интеграция Web3
    Реальные Проекты
    Кастомизация
    Branding
    Темы оформления
    Features
    Localization
    Настройка токеномики
    Web3
    Token launch jurisdictions
    Интеграции
    Ethereum-кошельки (Wagmi v3)
    Развёртывание
    Self-hosted развёртывание
    Vercel
    Docker
    Конфигурация Окружения
    Monitoring
    Оптимизация производительности
    Backup
    Архитектура
    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 Platform
    Краткий справочник
    С чего начать
    Предварительные требования
    Установка
    Первый успех
    Следующие шаги
    Функции
    Store
    Склад и остатки
    Управление вендорами
    Комиссии и расчёты
    SubscriptionConductor
    PaymentConductor
    Интеграция платежей
    Интеграция WayForPay
    Web3 Кошелек
    Реферальные коды (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкинга Токенов
    Entities
    Opportunities
    Messaging
    Модуль Новостей - Цифровой Газетный Опыт
    Блоги участников
    Система Резервирования Имён Пользователей
    Научный редактор
    Notifications
    Push-уведомления через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безопасность и соответствие
    Система локалей
    Мобильный Опыт
    Паттерны Оптимизации Производительности
    Примеры
    Quick Start
    White Label
    Интеграция Web3
    Реальные Проекты
    Кастомизация
    Branding
    Темы оформления
    Features
    Localization
    Настройка токеномики
    Web3
    Token launch jurisdictions
    Интеграции
    Ethereum-кошельки (Wagmi v3)
    Развёртывание
    Self-hosted развёртывание
    Vercel
    Docker
    Конфигурация Окружения
    Monitoring
    Оптимизация производительности
    Backup
    Архитектура
    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 Platform
    Краткий справочник
    С чего начать
    Предварительные требования
    Установка
    Первый успех
    Следующие шаги
    Функции
    Store
    Склад и остатки
    Управление вендорами
    Комиссии и расчёты
    SubscriptionConductor
    PaymentConductor
    Интеграция платежей
    Интеграция WayForPay
    Web3 Кошелек
    Реферальные коды (Refcodes)
    NFT Exhibition Marketplace
    Система Стейкинга Токенов
    Entities
    Opportunities
    Messaging
    Модуль Новостей - Цифровой Газетный Опыт
    Блоги участников
    Система Резервирования Имён Пользователей
    Научный редактор
    Notifications
    Push-уведомления через FCM (Ring)
    Email AI-CRM
    Протокол Tunnel
    Authentication
    Безопасность и соответствие
    Система локалей
    Мобильный Опыт
    Паттерны Оптимизации Производительности
    Примеры
    Quick Start
    White Label
    Интеграция Web3
    Реальные Проекты
    Кастомизация
    Branding
    Темы оформления
    Features
    Localization
    Настройка токеномики
    Web3
    Token launch jurisdictions
    Интеграции
    Ethereum-кошельки (Wagmi v3)
    Развёртывание
    Self-hosted развёртывание
    Vercel
    Docker
    Конфигурация Окружения
    Monitoring
    Оптимизация производительности
    Backup
    Архитектура
    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).*)"],
    }