mcpField
E
electric-proxy-auth

electric-proxy-auth

github.com/electric-sql開発10.4k54 回インストール3 週間前に更新
リポジトリ

electric-proxy-auth について

Electric の認証プロキシを設定する開発者向けのスキルです。テナント単位の Shape、パラメーター転送の制御、トークン更新、プロキシ応答の処理を扱います。

主な機能

  1. 01テナント単位の Shape 設定
  2. 02プロトコルパラメーターの許可リスト
  3. 03401 時のトークン更新
  4. 04CORS レスポンスヘッダーの設定
  5. 05シークレットと SQL パラメーターの安全な扱い

活用シーン

  1. 01サーバー側 Electric プロキシの構築
  2. 02テナント別の Shape データ制限
  3. 03ShapeStream 用トークンの更新
  4. 04危険なパラメーター転送の防止

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/electric-sql/electric --skill electric-proxy-auth

インストール後、Claude Code で/electric-proxy-authを入力して読み込まれたか確認します。

詳細

このスキルは electric-shapes を基盤としています。ShapeStream の設定については、まずそちらをお読みください。

Electric — プロキシと認証

セットアップ

import { ELECTRIC_PROTOCOL_QUERY_PARAMS } from '@electric-sql/client'

// Server route (Next.js App Router example)
export async function GET(request: Request) {
  const url = new URL(request.url)
  const originUrl = new URL('/v1/shape', process.env.ELECTRIC_URL)

  // Only forward Electric protocol params — never table/where from client
  url.searchParams.forEach((value, key) => {
    if (ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {
      originUrl.searchParams.set(key, value)
    }
  })

  // Server decides shape definition
  originUrl.searchParams.set('table', 'todos')
  originUrl.searchParams.set('secret', process.env.ELECTRIC_SOURCE_SECRET!)

  const response = await fetch(originUrl)
  const headers = new Headers(response.headers)
  headers.delete('content-encoding')
  headers.delete('content-length')

  return new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers,
  })
}

クライアントでの使用方法:

import { ShapeStream } from '@electric-sql/client'

const stream = new ShapeStream({
  url: '/api/todos', // Points to your proxy, not Electric directly
})

主なパターン

WHERE パラメーターによるテナント分離
// In proxy route — inject user context server-side
const user = await getAuthUser(request)
originUrl.searchParams.set('table', 'todos')
originUrl.searchParams.set('where', 'org_id = $1')
originUrl.searchParams.set('params[1]', user.orgId)
401 時の認証トークンの更新
const stream = new ShapeStream({
  url: '/api/todos',
  headers: {
    Authorization: async () => `Bearer ${await getToken()}`,
  },
  onError: async (error) => {
    if (error instanceof FetchError && error.status === 401) {
      const newToken = await refreshToken()
      return { headers: { Authorization: `Bearer ${newToken}` } }
    }
    return {}
  },
})
クロスオリジンプロキシの CORS 設定
// In proxy response headers
headers.set(
  'Access-Control-Expose-Headers',
  'electric-offset, electric-handle, electric-schema, electric-cursor'
)
サブセットのセキュリティ(AND のセマンティクス)

Electric は、メインシェイプの WHERE(プロキシで設定)とサブセットの WHERE(POST 本文から取得)を AND で結合します。サブセットで結果を絞り込むことはできますが、範囲を広げることはできません。

-- Main shape: WHERE org_id = $1 (set by proxy)
-- Subset: WHERE status = 'active' (from client POST)
-- Effective: WHERE org_id = $1 AND status = 'active'

サブセットに WHERE 1=1 を指定しても、メインシェイプの WHERE を回避することはできません。

よくある間違い

CRITICAL クライアントのすべてのパラメーターを Electric に転送する

誤り:

url.searchParams.forEach((value, key) => {
  originUrl.searchParams.set(key, value)
})

正しい例:

import { ELECTRIC_PROTOCOL_QUERY_PARAMS } from '@electric-sql/client'

url.searchParams.forEach((value, key) => {
  if (ELECTRIC_PROTOCOL_QUERY_PARAMS.includes(key)) {
    originUrl.searchParams.set(key, value)
  }
})
originUrl.searchParams.set('table', 'todos')

すべてのパラメーターを転送すると、クライアントが table、where、columns を制御でき、任意の Postgres テーブルにアクセスできてしまいます。転送するのは ELECTRIC_PROTOCOL_QUERY_PARAMS のみです。

ソース:examples/proxy-auth/app/shape-proxy/route.ts

CRITICAL content-encoding ヘッダーと content-length ヘッダーを削除しない

誤り:

return new Response(response.body, {
  status: response.status,
  headers: response.headers,
})

正しい例:

const headers = new Headers(response.headers)
headers.delete('content-encoding')
headers.delete('content-length')
return new Response(response.body, { status: response.status, headers })

fetch() はレスポンス本文を展開しますが、元の content-encoding ヘッダーと content-length ヘッダーはそのまま保持するため、ブラウザーでデコードに失敗します。

ソース:examples/proxy-auth/app/shape-proxy/route.ts:49-56

CRITICAL ELECTRIC_SECRET または SOURCE_SECRET をブラウザーに公開する

誤り:

// Client-side code
const url = `/v1/shape?table=todos&secret=${import.meta.env.VITE_ELECTRIC_SOURCE_SECRET}`

正しい例:

// Server proxy only
originUrl.searchParams.set('secret', process.env.ELECTRIC_SOURCE_SECRET!)

Vite などのバンドラーは、VITE_* 環境変数をクライアントコードに公開します。シークレットはプロキシ内でサーバー側からのみ注入してください。

ソース:AGENTS.md:17-20

CRITICAL 文字列補間による WHERE 句の SQL インジェクション

誤り:

originUrl.searchParams.set('where', `org_id = '${user.orgId}'`)

正しい例:

originUrl.searchParams.set('where', 'org_id = $1')
originUrl.searchParams.set('params[1]', user.orgId)

WHERE 句で文字列補間を使うと、SQL インジェクションが可能になります。位置パラメーター($1、$2)を使用してください。

ソース:website/docs/guides/auth.md

HIGH Bun の fetch 同時実行数の上限(256)が高負荷時にプロキシのボトルネックになる

誤り:

bun run proxy.ts

正しい例:

BUN_CONFIG_MAX_HTTP_REQUESTS=4096 bun run proxy.ts

Bun はデフォルトで、fetch() の同時呼び出し数を プロセスあたり 256 件に制限します。上限を超えたリクエストは通知なくキューに入ります。プロキシはインバウンド接続の受け付けを続けますが、Electric へのアップストリーム呼び出しが停滞し、エラーではなくレイテンシの急増として現れます。想定される同時シェイプリクエスト数に合わせて BUN_CONFIG_MAX_HTTP_REQUESTS を増やしてください(最大 65,336)。Node と Deno にはこの上限はありません。

ソース:https://bun.com/docs/runtime/networking/fetch

HIGH CORS 経由で Electric のレスポンスヘッダーを公開しない

誤り:

// No CORS header configuration — browser strips custom headers
return new Response(response.body, { headers })

正しい例:

headers.set(
  'Access-Control-Expose-Headers',
  'electric-offset, electric-handle, electric-schema, electric-cursor'
)
return new Response(response.body, { headers })

CORS によって Electric のレスポンスヘッダーが取り除かれると、クライアントは MissingHeadersError をスローします。electric-offset、electric-handle、electric-schema、electric-cursor を公開してください。

ソース:packages/typescript-client/src/error.ts:109-118

CRITICAL 本番環境のクライアントから Electric を直接呼び出す

誤り:

new ShapeStream({
  url: 'https://my-electric.example.com/v1/shape',
  params: { table: 'todos' },
})

正しい例:

new ShapeStream({
  url: '/api/todos', // Your proxy route
})

Electric の HTTP API はデフォルトで認証なしの公開状態です。シェイプ定義をサーバーが管理し、シークレットを注入できるように、必ずサーバー経由でプロキシしてください。

ソース:AGENTS.md:19-20

関連項目:electric-shapes/SKILL.md — シェイプ URL は Electric を直接指すのではなく、プロキシのルートを指す必要があります。 関連項目:electric-deployment/SKILL.md — 本番環境では ELECTRIC_SECRET とプロキシが必要です。開発環境では ELECTRIC_INSECURE=true を使用します。 関連項目:electric-postgres-security/SKILL.md — プロキシがシークレットを注入し、Postgres のセキュリティ機能がそれを適用します。

バージョン

@electric-sql/client v1.5.10 を対象としています。

コメント