Skip to Content
ПосібникиЗовнішня автентифікація з SIWX JWT

Інтеграція входу SIWX із зовнішніми сервісами через JWT та JWKS

Сесія SIWX за замовчуванням зберігається в захищеній HttpOnly куці: доступ до неї має виключно ваш власний сервер. Проте багато зовнішніх платформ (наприклад, провайдери вбудованих гаманців із кастомною автентифікацією, такі як Coinbase CDP , зовнішні IdP або ваші власні мікросервіси) приймають авторизацію лише у вигляді JWT-токенів, підписаних вашими публічними ключами.

Цей посібник зв'язує їх в єдину безшовну систему. За підсумком ви отримаєте:

  • GET /api/siwx/token — видає короткоживучий JWT для гаманця, що увійшов через SIWX.
  • GET /api/siwx/jwks — публікує криптографічні публічні ключі для перевірки підпису.
  • Підключення Coinbase CDP Custom Auth, який створює вбудований смарт-гаманець для авторизованого користувача.
  • Власний мікросервіс, що надійно довіряє токену без прямого доступу до бази сесій.
Крок налаштування маршруту містить вкладки EVM та Solana, синхронізовані з іншими посібниками.

Архітектура та життєвий цикл

Wallet ──signs CAIP-122──▶ POST /api/siwx/verify ──▶ session cookieBrowser ──cookie──▶ GET /api/siwx/token ──▶ JWT (ES256, 10 minutes)Provider ──▶ GET /api/siwx/jwks ──▶ public keys ──▶ checks the JWT signature, iss, aud, expProvider ──▶ finds or creates the user of `sub`

Перед початком

  • Додаток з посібника Full-Stack React, з маршрутами SIWX та файлом src/lib/authStores.ts.
  • Публічний HTTPS URL для додатку: зовнішній провайдер завантажує ваш JWKS за цією адресою.

Крок 1: Генерація приватного ключа підпису

Виконайте цей скрипт один раз і збережіть отриманий приватний JWK у секретах середовища вашого проекту. За замовчуванням використовується сучасний алгоритм ES256 (крива P-256):

scripts/generate-siwx-jwt-key.mjs
import { generateSiwxJwtKey } from '@tuwaio/sdk/siwx/server'; const { privateJwk, kid } = await generateSiwxJwtKey('ES256');console.log(`kid: ${kid}`);console.log(`SIWX_JWT_PRIVATE_KEY='${JSON.stringify(privateJwk)}'`);
node scripts/generate-siwx-jwt-key.mjs

Збережіть виведений рядок у змінній оточення SIWX_JWT_PRIVATE_KEY у файлі .env.local та в панелі вашого хостингу.


Крок 2: Увімкнення JWT у маршрутизаторі API

Опція jwt автоматично додає обробку маршрутів /token та /jwks до вашого обробника сесій SIWX:

src/app/api/siwx/[...siwx]/route.ts
import type { EvmVerifyClient } from '@tuwaio/evm-sdk/siwx';import { importSiwxJwtKey } from '@tuwaio/sdk/siwx/server';import { createSiwxApiHandler } from '@tuwaio/sdk/siwx/server-next';import { createPublicClient, http } from 'viem'; import { appChains } from '@/configs/appConfig';import { nonceStore, sessionStore } from '@/lib/authStores'; const appUrl = new URL(process.env.NEXT_PUBLIC_APP_URL ?? 'http://localhost:3000'); const clients = new Map<number, EvmVerifyClient>( appChains.map((chain) => [chain.id, createPublicClient({ chain, transport: http() })]),); export const { GET, POST, DELETE } = createSiwxApiHandler({ sessionStore, nonceStore, policy: { expectedDomain: appUrl.host, expectedUri: appUrl.origin, requireExpirationTime: true, maxIssuedAtAgeSeconds: 300, }, verifyOptions: { publicClient: (chainId) => clients.get(chainId) }, jwt: { signingKey: importSiwxJwtKey({ privateKey: process.env.SIWX_JWT_PRIVATE_KEY ?? '' }), issuer: appUrl.origin, audience: 'my-app', },});
  • GET /api/siwx/token повертає { token, expiresAt } для поточної сесії із заголовком Cache-Control: no-store (або 401, якщо сесія відсутня).
  • GET /api/siwx/jwks віддає набір публічних ключів із кешуванням на 5 хвилин.

Крок 3: Підключення Coinbase CDP Custom Authentication

У панелі керування CDP Portal  перейдіть у налаштування кастомної автентифікації вашого проекту та вкажіть параметри:

Параметр CDPЗначення
JWKS endpoint URLhttps://<ваш-домен>/api/siwx/jwks
Issuer (iss)URL вашого додатку, наприклад https://<ваш-домен>
Audience (aud)Значення audience з налаштувань (my-app)
User identifier claimЗалиште порожнім: за замовчуванням CDP використовує стандартне поле sub

Створіть клієнтський хелпер для отримання актуального токена:

src/lib/siwxJwt.ts
export async function getSiwxJwt(): Promise<string | undefined> { const res = await fetch('/api/siwx/token', { cache: 'no-store' }); if (!res.ok) return undefined; const { token } = (await res.json()) as { token: string; expiresAt: number }; return token;}

Огорніть додаток у провайдер Coinbase CDP:

src/providers/CdpProvider.tsx
'use client'; import { CDPHooksProvider, useAuthenticateWithJWT, useIsSignedIn } from '@coinbase/cdp-hooks';import { useSiwxSession } from '@tuwaio/sdk/siwx';import { type ReactNode, useEffect } from 'react'; import { getSiwxJwt } from '@/lib/siwxJwt'; function CdpSignIn() { const { isAuthenticated } = useSiwxSession(); const { isSignedIn } = useIsSignedIn(); const { authenticateWithJWT } = useAuthenticateWithJWT(); useEffect(() => { if (isAuthenticated && !isSignedIn) void authenticateWithJWT(); }, [isAuthenticated, isSignedIn, authenticateWithJWT]); return null;} export function CdpProvider({ children }: { children: ReactNode }) { return ( <CDPHooksProvider config={{ projectId: process.env.NEXT_PUBLIC_CDP_PROJECT_ID ?? '', customAuth: { getJwt: getSiwxJwt }, ethereum: { createOnLogin: 'eoa' }, }} > <CdpSignIn /> {children} </CDPHooksProvider> );}

Крок 4: Перевірка токена у власних бекенд-сервісах

Будь-який мікросервіс може автономно верифікувати токен за допомогою функції verifySiwxJwt:

services/orders/src/auth.ts
import { type SiwxJwks, verifySiwxJwt } from '@tuwaio/sdk/siwx/server'; const APP_URL = 'https://app.example.com'; export async function walletOf(request: Request): Promise<string | null> { const token = request.headers.get('authorization')?.replace(/^Bearer /, ''); if (!token) return null; const jwks = (await (await fetch(`${APP_URL}/api/siwx/jwks`)).json()) as SiwxJwks; const claims = await verifySiwxJwt(token, { jwks, issuer: APP_URL, audience: 'my-app' }); return claims?.sub ?? null;}

Клейми всередині виданого токена

КлеймПризначення та зміст
issВидавець токена (URL вашого додатку)
audЦільова аудиторія, якщо була налаштована
subІдентифікатор користувача: прив'язаний ID або адреса гаманця (у нижньому регістрі для EVM, Base58 для Solana)
caip10Повний ідентифікатор гаманця в стандарті CAIP-10 (наприклад, eip155:8453:0xAb58...)
chain_idCAIP-2 ідентифікатор мережі під час входу

Безпека та ротація ключів

  • Короткий час життя токенів: За замовчуванням 10 хвилин достатньо. Ніколи не зберігайте токени в localStorage.
  • Зберігайте ключ на сервері: SIWX_JWT_PRIVATE_KEY — це строгий секрет розгортання.
  • Ротація без простоїв: Згенеруйте новий ключ, призначте його як signingKey, а старий публічний перемістіть у jwt.previousKeys.

Локальна розробка

Зовнішній провайдер завантажує ваш JWKS через інтернет, тому http://localhost:3000 для нього недоступний. Використовуйте публічний тунель (ngrok або cloudflare) або тестове середовище для повної перевірки потоку CDP.


📚 Наступні кроки

Востаннє оновлено