Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)

    Documentation

    Concepts, value, and typical clone scenarios — less code.

    Welcome to Ring
    Quick Reference
    Getting Started
    Prerequisites
    Installation
    First Success Validation
    Next Steps
    Features
    Multi-Vendor Store
    Inventory & Stock
    Vendor Management
    Commissions & Settlements
    SubscriptionConductor
    PaymentConductor
    Payments Overview
    Public Pools & DAO Jars
    WayForPay Payment Integration
    Wallet & Credit System
    WalletConductor
    Affiliate & Referral Enablement
    Referral Codes (Refcodes)
    NFT Exhibition Marketplace
    Solana NFT Gates
    Token Staking System
    Owner Project Lab
    Entities
    Opportunities
    Real-Time Messaging
    Ring Tasks
    WebRTC Calls & STUNner TURN
    Peer Games
    News Module
    Member Blogs
    Public Profile Pages
    Ring File Cabinet
    Username Reservation System
    Scientific Editor
    Notifications
    Push Notifications with FCM (Ring-Powered)
    Email AI-CRM
    Ring Mailer & RingdomX Mail
    Tunnel Protocol
    VideoConductor
    MediaConductor
    Generative Gallery
    Authentication
    Security & Compliance
    Admin console
    Admin Wiki
    Manage via Telegram
    Locale System
    Mobile Experience
    Performance Optimization Patterns
    Examples
    Quick Start
    Basic Setup
    White Label
    Custom Branding
    Web3 Integration
    Real World
    Advanced Features
    Customization
    Quick Start — Your First Ring Clone
    Customization Guide
    Branding
    Themes
    Features
    Localization
    Token Economics Setup
    Payment Gateway Integration
    Reference Ring deployments
    Web3
    Token launch jurisdictions
    Wallet
    Wallet Security Tips
    Integrations
    Ethereum wallets (Wagmi v3)
    RingFileBase (object storage API)
    Ring CDN (RingFileBase edge)
    Deployment
    Self-hosted deployment
    Vercel
    Docker
    Environment Configuration
    Monitoring & Analytics
    Performance Optimization
    Backup & Recovery
    Architecture
    Data Model
    Security
    Real Time
    Discovery Mutation Sync
    PaymentConductor architecture
    WalletConductor architecture
    Development
    Ring MCP Server

    Quick entry (CTOs · auditors · agents)

    Welcome — mission & audiences
    Quick Reference
    Getting started
    Architecture & Auth.js
    Backend modes & databases (DB_BACKEND_MODE)
    Self-hosted
    Ring MCP Tools
    Ring MCP Server
    Token economics
    Token launch jurisdictions
    Deploy (Docker · k8s)
    Security & compliance reads
    ringdom.org — LegioX homebase
    Source — MIT license (GitHub)
    1. Docs
    2. /Examples
    3. /Authentication Examples

    Updated Jul 20, 20263 min listen

    Ring Platform Logo

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

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

    1. Docs
    2. /Examples
    3. /Authentication Examples

    Updated Jul 20, 20263 min listen

    Ring Platform Logo

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

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

    1. Docs
    2. /Examples
    3. /Authentication Examples

    Updated Jul 20, 20263 min listen

    Ring Platform Logo

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

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

    Authentication Examples

    Complete authentication implementation patterns using Auth.js v5 with Ring Platform.

    Use Founder / Developer tabs in the docs sidebar to filter this page. See Authentication, Authentication Architecture, Ring Mailer, and Environment Variables.

    Provider overview

    Ring Platform supports these Auth.js v5 sign-in paths:

    ProviderWhat it enables
    Google OAuthTraditional OAuth redirect + Google One Tap (GIS with server-side JWT verification)
    Telegram (web)Login via Telegram OIDC (oauth.telegram.org) — same-tab redirect button on /login
    Telegram Mini AppSilent session from Telegram.WebApp.initData via Credentials telegram-miniapp
    Apple Sign-InNative iOS/macOS sign-in via OAuth redirect
    Ring MailerOTP, magic link (/verify#token=…), password — own SMTP or Ethereal. See Ring Mailer
    Crypto WalletNonce-signature verification via Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTMachine-to-machine tokens for WebSocket and MCP gateway auth

    Session strategy: JWT (no server-side session store). 30-day max age, 24-hour update window.

    The database adapter (PostgreSQL or Firebase) is selected automatically by DB_BACKEND_MODE. For details, see Backend Modes and Databases.

    Auth.js v5 server configuration

    Canonical providers live in root auth.ts. Shape (illustrative — prefer reading the file):

    Request OTP / magic link via app/_actions/auth-email-actions.ts, then signIn('email-otp') or signIn('email-magic'). Do not import next-auth/providers/resend.

    Telegram sign-in (client) — web OIDC

    Prefer the shipped button (locale + buildOAuthCallbackUrl):

    Requires AUTH_TELEGRAM_ID / AUTH_TELEGRAM_SECRET. BotFather Allowed URL must include {origin}/api/auth/callback/telegram.

    Telegram Mini App sign-in (client)

    Call from a WebApp page that has loaded Telegram’s script (white-label shells may add /tg-mini-app; platform ships auth only):

    Requires a bot token reachable via getTelegramMiniAppBotToken() (TELEGRAM_MINI_APP_BOT_TOKEN preferred).

    Firebase credential strategy

    Firebase Admin SDK uses Application Default Credentials (ADC) first. The cert() fallback with explicit service-account credentials is only used when AUTH_FIREBASE_CLIENT_EMAIL and AUTH_FIREBASE_PRIVATE_KEY are present:

    Adapter selection

    DB_BACKEND_MODEAdapter

    Related documentation

    Related documentation

    Authentication

    Prerequisite: shipped providers, BotFather checklist, Mini App, and FutureFeature backlog.

    Authentication Architecture

    Deep-dive: file split, OIDC + Mini App modules, and adapters.

    SubscriptionConductor

    Next-step: telegram_stars invoices reuse the Mini App bot token helper.

    Apple Sign-in Integration

    Same-workflow: Apple-specific JWT / Services ID walkthrough.

    Authentication Examples

    Complete authentication implementation patterns using Auth.js v5 with Ring Platform.

    Use Founder / Developer tabs in the docs sidebar to filter this page. See Authentication, Authentication Architecture, Ring Mailer, and Environment Variables.

    Provider overview

    Ring Platform supports these Auth.js v5 sign-in paths:

    ProviderWhat it enables
    Google OAuthTraditional OAuth redirect + Google One Tap (GIS with server-side JWT verification)
    Telegram (web)Login via Telegram OIDC (oauth.telegram.org) — same-tab redirect button on /login
    Telegram Mini AppSilent session from Telegram.WebApp.initData via Credentials telegram-miniapp
    Apple Sign-InNative iOS/macOS sign-in via OAuth redirect
    Ring MailerOTP, magic link (/verify#token=…), password — own SMTP or Ethereal. See Ring Mailer
    Crypto WalletNonce-signature verification via Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTMachine-to-machine tokens for WebSocket and MCP gateway auth

    Session strategy: JWT (no server-side session store). 30-day max age, 24-hour update window.

    The database adapter (PostgreSQL or Firebase) is selected automatically by DB_BACKEND_MODE. For details, see Backend Modes and Databases.

    Auth.js v5 server configuration

    Canonical providers live in root auth.ts. Shape (illustrative — prefer reading the file):

    Request OTP / magic link via app/_actions/auth-email-actions.ts, then signIn('email-otp') or signIn('email-magic'). Do not import next-auth/providers/resend.

    Telegram sign-in (client) — web OIDC

    Prefer the shipped button (locale + buildOAuthCallbackUrl):

    Requires AUTH_TELEGRAM_ID / AUTH_TELEGRAM_SECRET. BotFather Allowed URL must include {origin}/api/auth/callback/telegram.

    Telegram Mini App sign-in (client)

    Call from a WebApp page that has loaded Telegram’s script (white-label shells may add /tg-mini-app; platform ships auth only):

    Requires a bot token reachable via getTelegramMiniAppBotToken() (TELEGRAM_MINI_APP_BOT_TOKEN preferred).

    Firebase credential strategy

    Firebase Admin SDK uses Application Default Credentials (ADC) first. The cert() fallback with explicit service-account credentials is only used when AUTH_FIREBASE_CLIENT_EMAIL and AUTH_FIREBASE_PRIVATE_KEY are present:

    Adapter selection

    DB_BACKEND_MODEAdapter

    Related documentation

    Related documentation

    Authentication

    Prerequisite: shipped providers, BotFather checklist, Mini App, and FutureFeature backlog.

    Authentication Architecture

    Deep-dive: file split, OIDC + Mini App modules, and adapters.

    SubscriptionConductor

    Next-step: telegram_stars invoices reuse the Mini App bot token helper.

    Apple Sign-in Integration

    Same-workflow: Apple-specific JWT / Services ID walkthrough.

    Authentication Examples

    Complete authentication implementation patterns using Auth.js v5 with Ring Platform.

    Use Founder / Developer tabs in the docs sidebar to filter this page. See Authentication, Authentication Architecture, Ring Mailer, and Environment Variables.

    Provider overview

    Ring Platform supports these Auth.js v5 sign-in paths:

    ProviderWhat it enables
    Google OAuthTraditional OAuth redirect + Google One Tap (GIS with server-side JWT verification)
    Telegram (web)Login via Telegram OIDC (oauth.telegram.org) — same-tab redirect button on /login
    Telegram Mini AppSilent session from Telegram.WebApp.initData via Credentials telegram-miniapp
    Apple Sign-InNative iOS/macOS sign-in via OAuth redirect
    Ring MailerOTP, magic link (/verify#token=…), password — own SMTP or Ethereal. See Ring Mailer
    Crypto WalletNonce-signature verification via Viem (Ethereum, Polygon, Arbitrum, Optimism, Base)
    Internal JWTMachine-to-machine tokens for WebSocket and MCP gateway auth

    Session strategy: JWT (no server-side session store). 30-day max age, 24-hour update window.

    The database adapter (PostgreSQL or Firebase) is selected automatically by DB_BACKEND_MODE. For details, see Backend Modes and Databases.

    Auth.js v5 server configuration

    Canonical providers live in root auth.ts. Shape (illustrative — prefer reading the file):

    Request OTP / magic link via app/_actions/auth-email-actions.ts, then signIn('email-otp') or signIn('email-magic'). Do not import next-auth/providers/resend.

    Telegram sign-in (client) — web OIDC

    Prefer the shipped button (locale + buildOAuthCallbackUrl):

    Requires AUTH_TELEGRAM_ID / AUTH_TELEGRAM_SECRET. BotFather Allowed URL must include {origin}/api/auth/callback/telegram.

    Telegram Mini App sign-in (client)

    Call from a WebApp page that has loaded Telegram’s script (white-label shells may add /tg-mini-app; platform ships auth only):

    Requires a bot token reachable via getTelegramMiniAppBotToken() (TELEGRAM_MINI_APP_BOT_TOKEN preferred).

    Firebase credential strategy

    Firebase Admin SDK uses Application Default Credentials (ADC) first. The cert() fallback with explicit service-account credentials is only used when AUTH_FIREBASE_CLIENT_EMAIL and AUTH_FIREBASE_PRIVATE_KEY are present:

    Adapter selection

    DB_BACKEND_MODEAdapter

    Related documentation

    Related documentation

    Authentication

    Prerequisite: shipped providers, BotFather checklist, Mini App, and FutureFeature backlog.

    Authentication Architecture

    Deep-dive: file split, OIDC + Mini App modules, and adapters.

    SubscriptionConductor

    Next-step: telegram_stars invoices reuse the Mini App bot token helper.

    Apple Sign-in Integration

    Same-workflow: Apple-specific JWT / Services ID walkthrough.

    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Server-side session

    Client-side session

    Session provider setup

    Ring wraps Auth.js with a tuned SessionProvider at features/auth/components/session-provider.tsx. Import this component — not SessionProvider directly from next-auth/react:

    Canonical settings: refetchInterval={15 * 60}, refetchOnWindowFocus={false}, refetchWhenOffline={false}.

    Environment variables

    Auth.js v5 reads AUTH_GOOGLE_*, AUTH_TELEGRAM_*, and AUTH_APPLE_* for OAuth/OIDC. Mini App auth uses the bot API token via TELEGRAM_MINI_APP_BOT_TOKEN (not the OIDC client secret). Email auth uses Ring Mailer (SMTP_* / EMAIL_MODE) — not AUTH_RESEND_KEY.

    Environment Configuration

    Depends-on: full env reference for clone secrets.

    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"
    import {
      isTelegramOidcConfigured,
      TelegramOidcProvider,
    } from "@/lib/auth/telegram-oidc"
    import {
      getTelegramMiniAppBotToken,
      verifyTelegramMiniAppInitData,
      isTelegramMiniAppAuthDateFresh,
    } from "@/lib/auth/telegram-miniapp-initdata"
    
    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: [
        CredentialsProvider({ id: "email-otp", /* email + code */ }),
        CredentialsProvider({ id: "email-magic", /* token */ }),
        CredentialsProvider({ id: "credentials", /* email + password */ }),
    
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        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 }
          },
        }),
    
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        ...(isTelegramOidcConfigured()
          ? [TelegramOidcProvider({ allowDangerousEmailAccountLinking: true })]
          : []),
    
        CredentialsProvider({
          id: "telegram-miniapp",
          name: "Telegram Mini App",
          credentials: { initData: { label: "Telegram initData", type: "text" } },
          async authorize(credentials) {
            const initData = String(credentials?.initData || "").trim()
            const botToken = getTelegramMiniAppBotToken()
            const parsed = verifyTelegramMiniAppInitData(initData, botToken)
            if (!parsed?.user?.id || !isTelegramMiniAppAuthDateFresh(parsed.authDate)) {
              return null
            }
            // resolveOrCreateTelegramUser(...) → return { id, email, name, image, role, telegramId }
          },
        }),
    
        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
          },
        }),
      ],
    })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    import { buildOAuthCallbackUrl } from "@/lib/auth/oauth-callback-url"
    
    await signIn("telegram", { callbackUrl: buildOAuthCallbackUrl(from, locale) })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    
    const initData = window.Telegram?.WebApp?.initData
    if (!initData) throw new Error("Not inside Telegram WebApp")
    
    const result = await signIn("telegram-miniapp", {
      initData,
      redirect: false,
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    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()
    }
    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Server-side session

    Client-side session

    Session provider setup

    Ring wraps Auth.js with a tuned SessionProvider at features/auth/components/session-provider.tsx. Import this component — not SessionProvider directly from next-auth/react:

    Canonical settings: refetchInterval={15 * 60}, refetchOnWindowFocus={false}, refetchWhenOffline={false}.

    Environment variables

    Auth.js v5 reads AUTH_GOOGLE_*, AUTH_TELEGRAM_*, and AUTH_APPLE_* for OAuth/OIDC. Mini App auth uses the bot API token via TELEGRAM_MINI_APP_BOT_TOKEN (not the OIDC client secret). Email auth uses Ring Mailer (SMTP_* / EMAIL_MODE) — not AUTH_RESEND_KEY.

    Environment Configuration

    Depends-on: full env reference for clone secrets.

    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"
    import {
      isTelegramOidcConfigured,
      TelegramOidcProvider,
    } from "@/lib/auth/telegram-oidc"
    import {
      getTelegramMiniAppBotToken,
      verifyTelegramMiniAppInitData,
      isTelegramMiniAppAuthDateFresh,
    } from "@/lib/auth/telegram-miniapp-initdata"
    
    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: [
        CredentialsProvider({ id: "email-otp", /* email + code */ }),
        CredentialsProvider({ id: "email-magic", /* token */ }),
        CredentialsProvider({ id: "credentials", /* email + password */ }),
    
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        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 }
          },
        }),
    
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        ...(isTelegramOidcConfigured()
          ? [TelegramOidcProvider({ allowDangerousEmailAccountLinking: true })]
          : []),
    
        CredentialsProvider({
          id: "telegram-miniapp",
          name: "Telegram Mini App",
          credentials: { initData: { label: "Telegram initData", type: "text" } },
          async authorize(credentials) {
            const initData = String(credentials?.initData || "").trim()
            const botToken = getTelegramMiniAppBotToken()
            const parsed = verifyTelegramMiniAppInitData(initData, botToken)
            if (!parsed?.user?.id || !isTelegramMiniAppAuthDateFresh(parsed.authDate)) {
              return null
            }
            // resolveOrCreateTelegramUser(...) → return { id, email, name, image, role, telegramId }
          },
        }),
    
        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
          },
        }),
      ],
    })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    import { buildOAuthCallbackUrl } from "@/lib/auth/oauth-callback-url"
    
    await signIn("telegram", { callbackUrl: buildOAuthCallbackUrl(from, locale) })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    
    const initData = window.Telegram?.WebApp?.initData
    if (!initData) throw new Error("Not inside Telegram WebApp")
    
    const result = await signIn("telegram-miniapp", {
      initData,
      redirect: false,
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    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()
    }
    k8s-postgres-fcmPostgreSQLAdapter()
    firebase-fullFirestoreAdapter(adminDb)
    supabase-fcmPostgreSQLAdapter()

    Server-side session

    Client-side session

    Session provider setup

    Ring wraps Auth.js with a tuned SessionProvider at features/auth/components/session-provider.tsx. Import this component — not SessionProvider directly from next-auth/react:

    Canonical settings: refetchInterval={15 * 60}, refetchOnWindowFocus={false}, refetchWhenOffline={false}.

    Environment variables

    Auth.js v5 reads AUTH_GOOGLE_*, AUTH_TELEGRAM_*, and AUTH_APPLE_* for OAuth/OIDC. Mini App auth uses the bot API token via TELEGRAM_MINI_APP_BOT_TOKEN (not the OIDC client secret). Email auth uses Ring Mailer (SMTP_* / EMAIL_MODE) — not AUTH_RESEND_KEY.

    Environment Configuration

    Depends-on: full env reference for clone secrets.

    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"
    import {
      isTelegramOidcConfigured,
      TelegramOidcProvider,
    } from "@/lib/auth/telegram-oidc"
    import {
      getTelegramMiniAppBotToken,
      verifyTelegramMiniAppInitData,
      isTelegramMiniAppAuthDateFresh,
    } from "@/lib/auth/telegram-miniapp-initdata"
    
    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: [
        CredentialsProvider({ id: "email-otp", /* email + code */ }),
        CredentialsProvider({ id: "email-magic", /* token */ }),
        CredentialsProvider({ id: "credentials", /* email + password */ }),
    
        GoogleProvider({
          allowDangerousEmailAccountLinking: true,
          checks: ["pkce", "state"],
        }),
    
        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 }
          },
        }),
    
        AppleProvider({
          allowDangerousEmailAccountLinking: true,
        }),
    
        ...(isTelegramOidcConfigured()
          ? [TelegramOidcProvider({ allowDangerousEmailAccountLinking: true })]
          : []),
    
        CredentialsProvider({
          id: "telegram-miniapp",
          name: "Telegram Mini App",
          credentials: { initData: { label: "Telegram initData", type: "text" } },
          async authorize(credentials) {
            const initData = String(credentials?.initData || "").trim()
            const botToken = getTelegramMiniAppBotToken()
            const parsed = verifyTelegramMiniAppInitData(initData, botToken)
            if (!parsed?.user?.id || !isTelegramMiniAppAuthDateFresh(parsed.authDate)) {
              return null
            }
            // resolveOrCreateTelegramUser(...) → return { id, email, name, image, role, telegramId }
          },
        }),
    
        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
          },
        }),
      ],
    })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    import { buildOAuthCallbackUrl } from "@/lib/auth/oauth-callback-url"
    
    await signIn("telegram", { callbackUrl: buildOAuthCallbackUrl(from, locale) })
    typescript
    
    "use client"
    import { signIn } from "next-auth/react"
    
    const initData = window.Telegram?.WebApp?.initData
    if (!initData) throw new Error("Not inside Telegram WebApp")
    
    const result = await signIn("telegram-miniapp", {
      initData,
      redirect: false,
    })
    typescript
    
    import { cert, initializeApp } from "firebase-admin/app"
    
    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>Please sign in</div>
      return <div>Welcome, {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>Loading...</div>
      if (!session) return <div>Not authenticated</div>
      return <div>User: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "@/features/auth/components/session-provider"
    
    export function AppClientShell({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }
    bash
    
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    AUTH_TELEGRAM_ID=your_telegram_oidc_client_id
    AUTH_TELEGRAM_SECRET=your_telegram_oidc_client_secret
    # TELEGRAM_MINI_APP_BOT_TOKEN=...  # Mini App initData HMAC
    
    AUTH_APPLE_ID=your_apple_client_id
    AUTH_APPLE_SECRET=your_apple_private_key
    
    # EMAIL_MODE=ethereal
    # SMTP_HOST= / SMTP_USER= / SMTP_PASSWORD= / SMTP_FROM=
    # OTP_HMAC_SECRET=
    typescript
    
    import { auth } from "@/auth"
    
    export default async function ProfilePage() {
      const session = await auth()
      if (!session) return <div>Please sign in</div>
      return <div>Welcome, {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>Loading...</div>
      if (!session) return <div>Not authenticated</div>
      return <div>User: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "@/features/auth/components/session-provider"
    
    export function AppClientShell({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }
    bash
    
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    AUTH_TELEGRAM_ID=your_telegram_oidc_client_id
    AUTH_TELEGRAM_SECRET=your_telegram_oidc_client_secret
    # TELEGRAM_MINI_APP_BOT_TOKEN=...  # Mini App initData HMAC
    
    AUTH_APPLE_ID=your_apple_client_id
    AUTH_APPLE_SECRET=your_apple_private_key
    
    # EMAIL_MODE=ethereal
    # SMTP_HOST= / SMTP_USER= / SMTP_PASSWORD= / SMTP_FROM=
    # OTP_HMAC_SECRET=
    typescript
    
    import { auth } from "@/auth"
    
    export default async function ProfilePage() {
      const session = await auth()
      if (!session) return <div>Please sign in</div>
      return <div>Welcome, {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>Loading...</div>
      if (!session) return <div>Not authenticated</div>
      return <div>User: {session.user.email}</div>
    }
    typescript
    
    "use client"
    import { SessionProvider } from "@/features/auth/components/session-provider"
    
    export function AppClientShell({ children }: { children: React.ReactNode }) {
      return <SessionProvider>{children}</SessionProvider>
    }
    bash
    
    AUTH_SECRET=your_auth_secret
    AUTH_TRUST_HOST=true
    
    AUTH_GOOGLE_ID=your_google_client_id
    AUTH_GOOGLE_SECRET=your_google_client_secret
    
    AUTH_TELEGRAM_ID=your_telegram_oidc_client_id
    AUTH_TELEGRAM_SECRET=your_telegram_oidc_client_secret
    # TELEGRAM_MINI_APP_BOT_TOKEN=...  # Mini App initData HMAC
    
    AUTH_APPLE_ID=your_apple_client_id
    AUTH_APPLE_SECRET=your_apple_private_key
    
    # EMAIL_MODE=ethereal
    # SMTP_HOST= / SMTP_USER= / SMTP_PASSWORD= / SMTP_FROM=
    # OTP_HMAC_SECRET=