eve-auth-and-secrets について
Eve Auth and Secrets は、Eve の認証、認証情報、シークレット、スコープ付きアクセスを管理するワークフロースキルです。ID、招待、OAuth トークン同期、アクセスグループ、アプリの SSO に関するコマンドライン操作と設定を扱います。
主な機能
- 01ログインしてトークンの権限を確認
- 02ID、招待、アクセス申請を管理
- 03ボットやサービスアカウント用のトークンを発行
- 04Claude と Codex の認証情報を指定スコープのシークレットに同期
- 05アクセスグループとスコープ付きポリシーを定義
- 06グループ対応のデータベース RLS を設定
活用シーン
- 01Eve の認証エラーを調査
- 02ユーザーを招待、または組織アクセスを申請
- 03ジョブやプロジェクト用に AI ツールの認証情報を同期
- 04グループとリソース範囲でデータアクセスを制限
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ incept5/ eve-skillpacks --skill eve-auth-and-secretsインストール後、Claude Code で/eve-auth-and-secretsを入力して読み込まれたか確認します。
同梱ファイルreferences/app-sso-integration.md
詳細
Eve 認証とシークレット
このワークフローを使用して Eve にログインし、アプリのシークレットを管理します。
使用する場面
- 新しいプロジェクトプロファイルの設定
- 認証の失敗
- シークレットの追加またはローテーション
- デプロイ時のシークレット補間エラー
- ID プロバイダーまたは組織への招待の設定
- Eve でデプロイしたアプリへの SSO ログインの追加
- アクセスグループとスコープ付きデータプレーン認可の設定
- 環境データベース用のグループ対応 RLS の設定
認証
eve auth login
eve auth login --ttl 30 # custom token TTL (1-90 days)
eve auth status
チャレンジレスポンスフロー
Eve はチャレンジレスポンス認証を使用します。デフォルトのプロバイダーは github_ssh です。
- クライアントが SSH 公開鍵のフィンガープリントを送信します
- サーバーがチャレンジ(ランダムなバイト列)を返します
- クライアントが秘密鍵でチャレンジに署名します
- サーバーが署名を検証し、JWT を発行します
トークンの種類
| 種類 | 発行方法 | 用途 |
|---|---|---|
| ユーザートークン | eve auth login | 対話型 CLI セッション |
| ジョブトークン | ワーカーが自動発行 | ジョブ内でのエージェント実行 |
| ミント済みトークン | eve auth mint | ボット/サービスアカウント |
JWT ペイロードには sub(ユーザー ID)、org_id、scope、exp が含まれます。JWKS エンドポイント GET /auth/jwks でトークンを検証します。
ロールと組織メンバーシップの変更はすぐに反映されます。サーバーは古い JWT クレームではなく、現在の DB メンバーシップから権限を解決します。リクエストに project_id が含まれ、org_id が含まれない場合、権限ガードはプロジェクトの所有組織から組織のコンテキストを導出します。
権限
現在のトークンで実行できる操作を確認します。
eve auth permissions
複数のプロバイダーでアクセスできるように、追加の ID を登録します。
curl -X POST "$EVE_API_URL/auth/identities" -H "Authorization: Bearer $TOKEN" \
-d '{"provider": "nostr", "external_id": "<pubkey>"}'
ID プロバイダー
Eve はプラグイン可能な ID プロバイダーに対応しています。認証ガードは、まず Bearer JWT を試し、次にプロバイダー固有のリクエスト認証を試します。
| プロバイダー | 認証方式 | 用途 |
|---|---|---|
github_ssh | SSH チャレンジレスポンス | デフォルトの CLI ログイン |
nostr | NIP-98 リクエスト認証+チャレンジレスポンス | Nostr ネイティブユーザー |
Nostr 認証
2 つの方法があります。
- チャレンジレスポンス:SSH と同様ですが、Nostr キーで署名します。
eve auth login --provider nostrを使用します。 - NIP-98 リクエスト認証:すべての API リクエストに Kind 27235 イベントで署名します。ステートレスで、保存済みトークンは不要です。
組織への招待
CLI または API から外部ユーザーを招待します。
# Invite with SSH key registration (registers key so the user can log in immediately)
eve admin invite --email [email protected] --ssh-key ~/.ssh/id_ed25519.pub --org org_xxx
# Invite with GitHub identity
eve admin invite --email [email protected] --github ghuser --org org_xxx
# Invite with web-based auth (Supabase)
eve admin invite --email [email protected] --web --org org_xxx
# API: invite targeting a Nostr pubkey
curl -X POST "$EVE_API_URL/auth/invites" -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"org_id": "org_xxx", "role": "member", "provider_hint": "nostr", "identity_hint": "<pubkey>"}'
認証方式(--github、--ssh-key、--web)が指定されていない場合、CLI はユーザーがログインできないことを警告します。ユーザーは後から eve auth request-access --org "Org Name" --ssh-key ~/.ssh/id_ed25519.pub --wait を使って自分で登録できます。
ID の認証が行われると、Eve はそのユーザーのアカウントと組織メンバーシップを自動的にプロビジョニングします。
アプリ主導のオンボーディングでは、従来の管理者向け招待フローではなく、組織スコープの招待 API を使用します。
# Create an org-scoped Supabase invite with a return URL for the app
curl -X POST "$EVE_API_URL/orgs/org_xxx/invites" \
-H "Authorization: Bearer $USER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "[email protected]",
"role": "member",
"redirect_to": "https://app.example.com/invite/complete",
"app_context": { "project_id": "proj_123" }
}'
# Search existing org members for an assignee picker
curl "$EVE_API_URL/orgs/org_xxx/members/search?q=ali" \
-H "Authorization: Bearer $USER_TOKEN"
これらの招待の作成または一覧表示には orgs:invite を持つユーザートークンを使用し、メンバーの検索には orgs:members:read を使用します。招待メールのリンク先は OAuth コールバックではなく、GoTrue の /verify パスにしてください。SSO の交換時に招待が自動適用される場合、Eve は invite_redirect_to を返します。これにより、メールプロバイダーがネストされたリダイレクトパラメーターを削除した場合でも、SSO コールバックからユーザーを対象アプリに戻せます。現在の招待オンボーディングでは、まず SSO セッションを確立し、次にユーザーを /set-password に誘導してからアプリへリダイレクトします。
アプリブランドの招待メール
プロジェクトはマニフェストの x-eve.branding を使って、アプリブランドの招待メールを有効にできます。件名、本文、From: の表示名にはすべてアプリの ID が反映されます。他のアプリでは、デフォルトで「Eve Horizon」が使用されます。
x-eve:
branding:
app_name: "ACME Portal"
app_logo_url: "https://app.example.com/assets/logo.svg" # https-only
primary_color: "#1f6feb"
email_from_name: "ACME Portal"
reply_to_email: "[email protected]"
support_email: "[email protected]"
support_url: "https://example.com/help"
編集後に eve project sync を実行します。eve org invite <email> --org <org_id> --project <project_id> で送信する招待には、プロジェクトのブランド設定が適用されます。フェーズ 1 では送信者アドレスはプラットフォームのデフォルトのままで、表示名のみが変わります。同じブランドテンプレートはマジックリンクログインメールにも使用されます。異なるのは文面(「招待を承認」か「サインイン」か)のみです。
トークンの発行(管理者)
SSH ログインなしでボット/サービスユーザー用のトークンを発行します。
# Mint token for a bot user (creates user + membership if needed)
eve auth mint --email [email protected] --org org_xxx
# With custom TTL (1-90 days, default: server configured)
eve auth mint --email [email protected] --org org_xxx --ttl 90
# Scope to project with admin role
eve auth mint --email [email protected] --project proj_xxx --role admin
現在のアクセストークンを出力します(スクリプトで便利です)。
eve auth token
セルフサービスのアクセスリクエスト
招待を受けていないユーザーは、アクセスをリクエストできます。
eve auth request-access --org "My Company" --email [email protected]
eve auth request-access --org "My Company" --ssh-key ~/.ssh/id_ed25519.pub
eve auth request-access --status <request_id>
管理者は次のコマンドで承認または拒否します。
eve admin access-requests list
eve admin access-requests approve <request_id>
eve admin access-requests reject <request_id> --reason "..."
リストの応答では、標準の { "data": [...] } エンベロープを使用します。
承認はアトミック(単一の DB トランザクション)であり、冪等です。完了済みのリクエストを再承認すると、既存のレコードが返されます。フィンガープリントがすでに登録されている場合、Eve はその ID の所有者を再利用します。リクエストされたスラッグと名前に一致するレガシーの不完全な組織がある場合、Eve は承認時にそれを再利用します。失敗した試行によって不完全な状態が残ることはありません。
認証情報の確認
ローカルの AI ツールの認証情報を検証します。
eve auth creds # Show Claude + Codex cred status
eve auth creds --claude # Only Claude
eve auth creds --codex # Only Codex
出力にはトークンタイプ(setup-token または oauth)、プレビュー、有効期限が含まれます。同期する前にトークンの状態を確認するために使用してください。
OAuth トークンの同期
ローカルの Claude/Codex OAuth トークンを Eve のシークレットに同期すると、エージェントが利用できるようになります。スコープの優先順位は、プロジェクト > 組織 > ユーザーです。
eve auth sync # Sync to user-level (default)
eve auth sync --org org_xxx # Sync to org-level (shared across org projects)
eve auth sync --project proj_xxx # Sync to project-level (scoped to one project)
eve auth sync --dry-run # Preview without syncing
これにより、指定したスコープに CLAUDE_CODE_OAUTH_TOKEN/CLAUDE_OAUTH_REFRESH_TOKEN(Claude)と CODEX_AUTH_JSON_B64(Codex/Code)が設定されます。
Claude トークンのタイプ
| トークンのプレフィックス | タイプ | 有効期間 | 推奨事項 |
|---|---|---|---|
sk-ant-oat01-* | setup-token(長期有効) | 長期有効 | ジョブと自動化に推奨 |
その他の sk-ant-* | oauth(短期有効) | 約15時間 | 対話型の開発に使用し、claude setup-token で再生成してください |
eve auth sync は、短期有効の OAuth トークンを同期する際に警告します。同期する前に eve auth creds を実行して、トークンのタイプを確認してください。
Codex/Code トークンの自動書き戻し
各ハーネスの呼び出し後、ワーカーはセッション中に Codex/Code CLI が auth.json を更新したかどうかを確認します。トークンが変更されていた場合、元のシークレットスコープ(ユーザー/組織/プロジェクト)に自動で書き戻されるため、次のジョブは新しいトークンで開始されます。この処理は透過的で、ジョブの結果には影響しません。書き戻しに失敗しても、警告がログに記録されるだけです。
Codex/Code の認証情報については、tokens.expires_at を比較して、~/.codex/auth.json と ~/.code/auth.json のうち最も新しいトークンを同期します。
アクセスグループとスコープ付きアクセス
グループは、データプレーンへのアクセス(組織のファイルシステム、組織のドキュメント、環境データベース)を分割する、主要な認可プリミティブです。グループを作成し、メンバーを追加し、スコープ制約付きでロールを割り当てます。
# Create a group
eve access groups create --org org_xxx --slug eng-team --name "Engineering"
# Add members
eve access groups members add eng-team --org org_xxx --user user_abc
eve access groups members add eng-team --org org_xxx --service-principal sp_xxx
# Bind a role with scoped access
eve access bind --org org_xxx --group grp_xxx --role data-reader \
--scope-json '{"orgfs":{"allow_prefixes":["/shared/"]},"envdb":{"schemas":["public"]}}'
# Check effective access
eve access memberships --org org_xxx --user user_abc
スコープの種類
| リソース | スコープフィールド | 例 |
|---|---|---|
| 組織のファイルシステム | orgfs.allow_prefixes、orgfs.read_only_prefixes | "/shared/"、"/reports/" |
| 組織のドキュメント | orgdocs.allow_prefixes、orgdocs.read_only_prefixes | "/pm/features/" |
| 環境 DB | envdb.schemas、envdb.tables | "public"、"analytics_*" |
グループ対応の RLS
環境データベースでグループベースの行レベルセキュリティを設定するための RLS ヘルパー関数を生成します。
eve db rls init --with-groups
これにより、Eve のランタイムが設定するセッションコンテキストを読み取る SQL ヘルパー(app.current_user_id()、app.current_group_ids()、app.has_group())が作成されます。RLS ポリシーで使用します。
CREATE POLICY notes_group_read ON notes FOR SELECT
USING (group_id = ANY(app.current_group_ids()));
メンバーシップの詳細確認
プリンシパルの有効なアクセス権限全体(基本的な組織/プロジェクトのロール、グループメンバーシップ、解決済みの割り当て、統合されたスコープ)を確認します。
eve access memberships --org org_xxx --user user_abc
eve access memberships --org org_xxx --service-principal sp_xxx
応答には、すべての割り当てを統合した effective_scopes、effective_permissions、および各割り当ての matched_via(直接またはグループ)が含まれます。
リソース固有のアクセスチェック
特定のデータプレーンリソースに対するアクセスを確認し、その理由を説明します。
eve access can orgfs:read /shared/reports --org org_xxx
eve access explain orgfs:write /shared/reports --org org_xxx --user user_abc
応答には scope_required、scope_matched、および各許可の scope_reason が含まれ、割り当てがリクエストされたリソースパスに一致したかどうかが説明されます。
ポリシー・アズ・コード(v2)
.eve/access.yaml でグループ、ロール、スコープ付き割り当てを宣言します。version: 2 を使用してください。
version: 2
access:
groups:
eng-team:
name: Engineering Team
description: Scoped access for engineering collaborators
members:
- type: user
id: user_abc
roles:
app_editor:
scope: org
permissions:
- orgdocs:read
- orgdocs:write
- orgfs:read
- envdb:read
bindings:
- subject: { type: group, id: eng-team }
roles: [app_editor]
scope:
orgdocs: { allow_prefixes: ["/groups/app/**"] }
orgfs: { allow_prefixes: ["/groups/app/**"] }
envdb: { schemas: ["app"] }
検証、プラン作成、同期を行います。
eve access validate --file .eve/access.yaml
eve access plan --file .eve/access.yaml --org org_xxx
eve access sync --file .eve/access.yaml --org org_xxx
同期は宣言的です。YAML に一致するように、グループ、メンバー、ロール、割り当てを作成、更新、削除します。無効なスコープ設定は、変更が適用される前に即座にエラーになります。割り当ての対象には user、service_principal、group を指定できます。
キーのローテーション
JWT 署名キーをローテーションします。
- 既存のシークレットと並行して
EVE_AUTH_JWT_SECRET_NEWを設定します - サーバーは新しいキーで署名を開始しますが、猶予期間中は両方のキーを受け入れます
- 猶予期間(
EVE_AUTH_KEY_ROTATION_GRACE_HOURS)の終了後、古いシークレットを削除します - 緊急ローテーション:新しいキーのみを設定します(既存のトークンはすべて直ちに無効になります)
アプリの SSO 統合
2 つの共有パッケージ、@eve-horizon/auth(バックエンド)と @eve-horizon/auth-react(フロントエンド)を使用して、Eve がデプロイした任意のアプリに Eve SSO ログインを追加できます。プラットフォームは、デプロイされたサービスに EVE_SSO_URL、EVE_ORG_ID、EVE_PROJECT_ID、EVE_API_URL を自動的に挿入します。
マジックリンクログインのオプトイン(パスワードレスアプリ)
アプリでは、x-eve.auth.login_method: magic_link を指定して、パスワードレスのブラウザログインをオプトインできます。SSO ログインページにはプロジェクトのブランドが適用され、ユーザー名とパスワードの代わりに、メールによるマジックリンクログインが表示されます。
x-eve:
auth:
login_method: magic_link # or password_or_magic_link, password
self_signup: false # unknown emails get generic success, no email
invite_requires_password: false # invite callback skips /set-password
マジックリンクメールは GoTrue に直接送信させるのではなく、Eve API が POST /auth/magic-link 経由で送信します。これにより、プラットフォームはプロジェクトポリシーを適用し、招待メールと x-eve.branding テンプレートを共有し、アカウントの列挙を防止できます。x-eve.auth を設定していないプロジェクトでは、従来の SSO 動作が維持されます。新しいユーザーは eve org invite <email> --org <org_id> --project <project_id> で作成します。
マジックリンク確認インタースティシャル(セキュリティ)
Eve がレンダリングするマジックリンクメールと招待メールには、生の GoTrue 確認 URL ではなく、ラップ URL(https://sso/m/mlw_<id>)が埋め込まれます。メールセキュリティスキャナー(Defender SafeLinks、Mimecast、Proofpoint、Barracuda)はメール内のすべての URL にアクセスします。そのため、ラップ URL がなければ、人がクリックする前に使い捨て OTP が消費されてしまいます。ラップ URL を開くと、プロジェクトのブランドが適用された「サインインを確認/招待を承諾」ページが表示されます。ボタンからの POST が行われた場合にのみ GoTrue URL が返され、302 リダイレクトされます。これはプラットフォームによる保証として扱ってください。アプリ側での対応は不要です。
ドメインベースのサインアップ(パス C の自動割り当て)
メールドメインを事前承認すると、該当するメールアドレスを持つユーザーは、ユーザーごとの招待なしでマジックリンクからサインインできます。初回のログインが成功すると、プラットフォームはそのユーザーをルールの target_org の member として割り当てます。1 つのプロジェクトで、異なるドメインを異なる組織に振り分けることができます。
x-eve:
auth:
login_method: magic_link
invite_requires_password: false
org_access:
mode: allowlist
allowed_orgs: [org_Acme, org_Partner, org_Retailer]
domain_signup:
enabled: true
domains:
- { domain: example.com, target_org: org_Acme, role: member }
- { domain: partner.example, target_org: org_Partner }
- { domain: retailer.example, target_org: org_Retailer }
ルールは宣言された順に確認され、最初に一致したものが適用されます。そのため、より具体的なパターンを先に宣言してください。各ルールの target_org は allowed_orgs に含まれている必要があります。フリーメールプロバイダー(free-mail.example)の宣言は許可されますが、マニフェストの整合性に関する警告が出ます。明示的な保留中の招待はドメインベースのサインアップより優先されます(パス B がパス C より優先されます)。ルールを削除すると新規サインアップは停止しますが、既存のメンバーシップは遡って削除されません。既存のメンバーシップは eve org members remove で削除してください。
イベントスパインを通じて監査できます。auth.domain_signup.invite_created と auth.domain_signup.member_attached には、org_id、matched_rule、matched_domain が含まれます。
アプリの組織アクセスと管理者による招待
アプリはデフォルトでプロジェクト所有者の組織へのアクセスを許可します。mode: allowlist を使用して、アプリを利用できる顧客組織を宣言し、ブランド付きマジックリンクのオンボーディングメールを送信するアプリ内管理者招待を有効にします。
x-eve:
auth:
org_access:
mode: allowlist
allowed_orgs: [org_customer123, customer-slug]
invite:
enabled: true
admin_roles: [admin, owner]
invited_role: member # fixed; app invites cannot create admins
エンドポイント:GET /auth/app-access はユーザーがアクセスできる組織と、ユーザーが招待できる組織を返します。POST /auth/app-invites を使用すると、組織の管理者またはオーナーが、プロジェクトのブランドが適用されたメールで通常のメンバーを招待できます。複数組織にまたがるアプリでは、バックエンドで eveUserAuth() の代わりに eveAppUserAuth() を使用してください。これは /auth/app-access を参照し、X-Eve-Org-Id、?eve_org_id=、または最初に許可された組織から組織を選択します。
プロジェクトスコープのリダイレクト許可リスト(カスタムドメインのアプリ)
SSO ブローカーは、デフォルトではクラスターのドメイン配下にあるリダイレクト先のみを受け付けます。独自ドメインにデプロイされたアプリでは、オリジンを宣言する必要があります。
x-eve:
auth:
allowed_redirect_origins:
- https://app.example.com
- https://www.example.com
エントリーはオリジンのみ(scheme://host[:port])です。パス、クエリ、フラグメントはマニフェストの検証時に拒否されます。GET /auth/app-context が返す最終的な許可リストは、(1)マニフェストで明示されたエントリー、(2)プロジェクト自身の対象となるカスタムドメイン(environment_id があり、ステータスが dns_verified/cert_provisioning/active の custom_domains 行)、(3)allowed_orgs 内のプロジェクトが所有する組織横断のカスタムドメインの和集合です。eve project auth-context <project_id> で確認できます。
これにより、クラスター外のオリジンに対するハードコードされた EVE_DEFAULT_DOMAIN の許可リストが置き換えられます。ブローカーはこのリストを、/callback の redirect_to 検証と、/session および /logout の CORS の両方に使用します。@eve-horizon/auth-react プロバイダーは、クロスサイト Cookie が正しくスコープ設定されるよう、セッションとログアウトの呼び出しに project_id を自動的に渡します。
カスタムドメインでの SameSite=None(プラットフォーム保証)
SSO が EVE_SSO_SECURE_COOKIES=true でデプロイされている場合、ブローカーは eve_sso_rt および eve_sso Cookie を Secure; SameSite=None 付きで発行します。アプリがカスタムドメインにある場合、React プロバイダーのクロスサイト fetch('/session', { credentials: 'include' }) プローブで Cookie を送信するために必要です。ローカルの k3d(http://*.lvh.me)では引き続き SameSite=Lax が使用されます。アプリ側で設定する必要はなくなりました。
承認済みのメールドメインにセルフサインアップを制限する
環境変数 EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS が設定されている場合、SSO サービスはメールドメインに基づいて /auth/signup と /auth/magiclink を制限します(カンマ区切り)。未設定の場合は、すべてのドメインが許可されます(デフォルト)。制限が有効な場合、SSO ログインページのサインアップタブにドメインのヒントが表示されます。
# On the SSO deployment, set:
EVE_SIGNUP_ALLOWED_EMAIL_DOMAINS=acme.com,partner.io
拒否されたリクエストは、error: email_domain_not_allowed を含む HTTP 422 を返します。これを使うと、セルフサインアップを完全に無効化せずに、公開 SSO エンドポイントをドメイン単位の招待制にできます。既存のアカウントと管理者による招待には影響しません。
バックエンド(@eve-horizon/auth)
インストール:npm install @eve-horizon/auth
新しいアプリでは、デフォルトで統合ミドルウェアを使用します。
| エクスポート | 動作 |
|---|---|
eveAuth() | ノンブロッキングミドルウェアです。ユーザーまたはエージェントのトークンを検証し、正規化された ID を req.eveIdentity に設定します。 |
eveIdentityGuard() | req.eveIdentity が設定されていない場合は 401 を返します。保護対象のルートに設定します。 |
eveAuthConfig() | 自動注入された環境変数から { sso_url, eve_api_url, ... } を返すハンドラーです。フロントエンドはこれを取得して SSO を検出します。 |
eveAuthMe() | React SDK とカスタムクライアント向けの /auth/me ハンドラーです。 |
ユーザーのみ、またはエージェントのみの処理を明示的に行うアプリでのみ、従来の分割ミドルウェアを使用します。
| エクスポート | 動作 |
|---|---|
eveUserAuth() | ユーザー専用のノンブロッキングミドルウェアです。req.eveUser: { id, email, orgId, role } を設定します。 |
eveAuthGuard() | req.eveUser が設定されていない場合は 401 を返します。 |
eveAuthMiddleware() | エージェント/ジョブトークン向けのブロッキングミドルウェアです。完全な EveTokenClaims を含む req.agent を設定します。失敗した場合は 401 を返します。 |
verifyEveToken(token) | JWKS によるローカル検証(キャッシュ期間は 15 分)を行います。EveTokenClaims を返します。 |
verifyEveTokenRemote(token) | /auth/token/verify 経由で HTTP 検証を行います。常に最新の状態を反映します。 |
Express の設定(約 3 行):
import { eveAuth, eveIdentityGuard, eveAuthConfig, eveAuthMe } from '@eve-horizon/auth';
app.use(eveAuth());
app.get('/auth/config', eveAuthConfig());
app.get('/auth/me', eveAuthMe()); // Full response for React SDK
app.use('/api', eveIdentityGuard());
req.eveIdentity は両方のトークンタイプを正規化します。
- ユーザートークン:
id、email、orgId、role、permissions、isAgent: false - エージェント/ジョブトークン:
jobId、agentSlug、安定したemail({agent_slug}@eve.agent)、permissions、isAgent: true
RLS、監査ログ、アプリレベルのルーティングには、agentSlug または安定したエージェントのメールアドレスを使用します。{job_id}@eve.agent をエージェント ID のキーとして使用しないでください。この旧形式はジョブごとに異なり、安定しません。
NestJS の設定:main.ts で eveAuth() をグローバルに適用し、薄いガードラッパーを使用します。
// main.ts
import { eveAuth } from '@eve-horizon/auth';
app.use(eveAuth());
// auth.guard.ts -- thin NestJS adapter
@Injectable()
export class EveGuard implements CanActivate {
canActivate(ctx: ExecutionContext): boolean {
const req = ctx.switchToHttp().getRequest();
if (!req.eveIdentity) throw new UnauthorizedException();
return true;
}
}
// auth-config.controller.ts
@Controller()
export class AuthConfigController {
private handler = eveAuthConfig();
@Get('auth/config')
getConfig(@Req() req, @Res() res) { this.handler(req, res); }
}
検証戦略:eveAuth() と eveUserAuth() のデフォルトは 'local'(JWKS、15 分間キャッシュ)です。リクエストごとに約 50 ミリ秒のレイテンシでメンバーシップの最新状態を反映するには、strategy: 'remote' を使用します。
カスタムロールのマッピング:Eve の owner/admin/member 以外のロールがアプリで必要な場合は、eveAuth() の後に連携処理を行います。
app.use((req, _res, next) => {
if (req.eveIdentity && !req.eveIdentity.isAgent) {
req.user = {
...req.eveIdentity,
appRole: req.eveIdentity.role === 'member' ? 'viewer' : 'admin',
};
}
next();
});
フロントエンド(@eve-horizon/auth-react)
インストール:npm install @eve-horizon/auth-react
| エクスポート | 目的 |
|---|---|
EveAuthProvider | コンテキストプロバイダーです。セッションを初期化し、sessionStorage を確認して、SSO /session に問い合わせ、トークンをキャッシュします。 |
useEveAuth() | フック:{ user, loading, error, config, loginWithSso, loginWithToken, logout } |
EveLoginGate | 認証済みの場合は子要素を、それ以外の場合はログインフォームをレンダリングします。 |
EveLoginForm | SSO とトークン貼り付けによるログイン UI を内蔵しています。 |
createEveClient(baseUrl?) | Bearer トークンを自動で挿入する Fetch ラッパーです。 |
シンプルな設定 -- EveLoginGate が読み込み、ログイン、認証済みの各状態を処理します。
import { EveAuthProvider, EveLoginGate } from '@eve-horizon/auth-react';
<EveAuthProvider apiUrl="/api">
<EveLoginGate>
<ProtectedApp />
</EveLoginGate>
</EveAuthProvider>
カスタム認証ゲート -- useEveAuth() を使用すると、読み込み、ログイン、エラーの各状態を細かく制御できます。
import { EveAuthProvider, useEveAuth } from '@eve-horizon/auth-react';
function AuthGate() {
const { user, loading, loginWithToken, loginWithSso, logout } = useEveAuth();
if (loading) return <Spinner />;
if (!user) return <LoginPage onSso={loginWithSso} onToken={loginWithToken} />;
return <App user={user} onLogout={logout} />;
}
export default () => (
<EveAuthProvider apiUrl="/api">
<AuthGate />
</EveAuthProvider>
);
認証付き API 呼び出し:Bearer トークンを自動で挿入するには、createEveClient() を使用します。
import { createEveClient } from '@eve-horizon/auth-react';
const client = createEveClient('/api');
const res = await client.fetch('/data');
カスタム認証からの移行
この SDK を使うと、手作業で実装した約 700~800 行の認証コードを約 50 行に置き換えられます。カスタムの JWKS/トークン検証、Bearer 抽出ミドルウェア、SSO URL の検出、セッション確認ロジック、トークン保存ヘルパー、ログインフォームを削除してください。アプリ固有のロールマッピングとローカルパスワード認証は残します。
移行チェックリスト、型のリファレンス、トークンのライフサイクル、応用パターン(SSE 認証、トークン貼り付けモード、トークンの陳腐化)については、references/app-sso-integration.md を参照してください。
デプロイ済みサービス向けのサービストークン
デプロイされたサービスにはそれぞれ、Eve API へのサーバー間コールに使用する EVE_SERVICE_TOKEN(90 日間有効な RS256 JWT、type: service)が自動的に注入されます。デプロイ時にデプロイヤーがトークンを発行するため、アプリでこのシークレットを手動で設定する必要はなくなりました。
トークンの権限は、デフォルトで読み取り専用(projects:read、jobs:read、threads:read、envs:read、secrets:read、builds:read、pipelines:read、agents:read、events:read)です。書き込みアクセスが必要なアプリは、マニフェストで追加の権限を明示的に宣言します。
services:
api:
x-eve:
permissions: [jobs:write, events:write, threads:write]
アプリから Eve API へのコール(ジョブの作成、イベントの送信、スレッドの更新)には、これを使用します。スキーマとコールパターンの詳細については、eve-read-eve-docs/references/secrets-auth.md と eve-read-eve-docs/references/manifest.md を参照してください。
BYOK モデル(LLM API キー)
Eve は推論トラフィックをプロキシしません。すべてのモデルアクセスは BYOK(Bring Your Own Keys)です。ハーネスとアプリはシークレット経由で独自の API キーを持ち込み、プロバイダーを直接呼び出します。
LLM プロバイダーのキーはプロジェクトシークレットとして保存します。
eve secrets set ANTHROPIC_API_KEY "sk-ant-xxx" --project proj_xxx
eve secrets set OPENAI_API_KEY "sk-xxx" --project proj_xxx
eve secrets set OPENAI_BASE_URL "https://my-vllm.runpod.ai/v1" --project proj_xxx
ハーネスはこれらを自動的に解決します。セルフホストモデル(vLLM、Tailscale 経由の LM Studio)の場合は、ベース URL と API キーをシークレットとして設定してください。Eve はプライベートエンドポイント経由で接続を提供します(eve-deploy-debugging を参照)。管理された推論レイヤーは提供しません。
組織ごとの OAuth 認証情報(BYOA)
各組織は、Google Drive、Slack、その他の連携機能向けに独自の OAuth アプリ認証情報を用意します。クラスター全体で共有されるシークレットはありません。
# View setup instructions (redirect URIs, required scopes)
eve integrations setup-info google-drive
eve integrations setup-info slack
# Register OAuth app credentials
eve integrations configure google-drive \
--client-id "xxx.apps.googleusercontent.com" \
--client-secret "GOCSPX-xxx" \
--label "Acme Corp Google Drive"
eve integrations configure slack \
--client-id "12345.67890" \
--client-secret "abc123" \
--signing-secret "def456" \
--app-id "A0123ABC" \
--label "Acme Corp Slack Bot"
# View current config (secrets redacted)
eve integrations config google-drive
# Then connect as before (uses per-org credentials)
eve integrations connect google-drive
eve integrations connect slack
利点:組織ごとに認証情報を分離でき、同意画面のブランディングをカスタマイズでき、レート制限を個別に適用でき、共有シークレットの影響範囲がありません。
プロジェクトのロール解決
ロールや組織メンバーシップの変更は即時に反映されます。サーバーは古い JWT クレームではなく、最新の DB メンバーシップから権限を解決します。リクエストに project_id が含まれ、org_id が含まれない場合、権限ガードはプロジェクトの所有組織から組織コンテキストを導出します。
Auth SDK(@eve-horizon/auth)では、eveUserAuth() ミドルウェアを通じてこれを利用できます。メンバーシップの変更を即時反映する必要がある場合は、strategy: 'remote' を使用します。
プロジェクトシークレット
# Set a secret
eve secrets set API_KEY "your-api-key" --project proj_xxx
# List keys (no values)
eve secrets list --project proj_xxx
# Delete a secret
eve secrets delete API_KEY --project proj_xxx
# Import from file
eve secrets import .env --project proj_xxx
シークレットの補間
${secret.KEY} を使用して .eve/manifest.yaml でシークレットを参照します。
services:
api:
environment:
API_KEY: ${secret.API_KEY}
マニフェストの検証
デプロイ前に、必須シークレットがすべて設定されていることを検証します。
eve manifest validate --validate-secrets # check secret references
eve manifest validate --strict # fail on missing secrets
ローカルシークレットファイル
ローカル開発では、.eve/dev-secrets.yaml を作成します(gitignore 対象)。
secrets:
default:
API_KEY: local-dev-key
DB_PASSWORD: local-password
staging:
DB_PASSWORD: staging-password
ワーカーへの注入
ジョブの実行時に、解決済みのシークレットが環境変数としてワーカーコンテナに注入されます。ファイル型のシークレットはディスクに書き込まれ、EVE_SECRETS_FILE 経由で参照されます。エージェントプロセスがファイルを読み取った後、ファイルは削除されます。
Git 認証
ワーカーはシークレットを使用してリポジトリにアクセスします。
- HTTPS:
github_tokenシークレット →Authorization: Bearerヘッダー - SSH:
ssh_keyシークレット →~/.ssh/に書き込まれ、GIT_SSH_COMMAND経由で使用
認証メールの配信(SES)
組織/アプリへの招待、アプリスコープのマジックリンク、システム管理者による Supabase への招待など、ブランド化された認証メールはすべて単一の MailerService を経由します。SMTP の接続先が SES の場合(GOTRUE_SMTP_HOST=*.amazonaws.com または EVE_MAILER_CHECK_SUPPRESSION=true)、メーラーは送信前に GetSuppressedDestination を確認するため、アカウントレベルの抑制によって送信が成功したように見えてしまうことはありません。
| 結果 | 動作 |
|---|---|
| アドレスが抑制されている | EmailSuppressedError をスローします。SMTP 送信は行いません |
| 見つからない | 送信を続行します |
| AWS エラー(IRSA、スロットリング、ネットワーク) | フェイルオープンします。mailer.suppression_check_failed をログに記録し、送信を続行します |
呼び出し元の動作:sendEligibleMagicLink は EmailSuppressedError を握りつぶして一般的な成功を返し(アカウント列挙への防御を維持)、mail.suppressed_drop をログに記録します。招待処理ではエラーを再スローするため、管理者にエラーが表示されます。
EVE_SES_CONFIGURATION_SET が設定されている場合、SES は Bounce/Complaint/Delivery/Reject イベントを SNS にルーティングし、SNS は /webhooks/ses-feedback に POST します。この Webhook は SNS 署名を検証し、TopicArn を EVE_SES_FEEDBACK_TOPIC_ARN と照合したうえで、影響を受けた受信者ごとに email_delivery_events に 1 行を保存します(sha256(snsMessageId|eventType|recipient) による冪等性)。
管理 CLI でイベントを確認します:
eve admin email bounces list
eve admin email bounces list --recipient [email protected]
eve admin email bounces list --event-type Bounce --limit 100 --json
ローカルテーブルから読み取るだけで、SES は変更しません。アカウントレベルの抑制を解除する方法については、SES の抑制ランブックを参照してください。
API Pod のログで検索する構造化ログイベント:mailer.sent、mailer.smtp_failed、mailer.suppressed、mailer.suppression_check_failed、mail.suppressed_drop、sns.subscription_confirmed、sns.rejected、ses.feedback_persisted。
トラブルシューティング
| 問題 | 解決方法 |
|---|---|
| 認証されていない | eve auth login を実行します |
| トークンの有効期限が切れている | eve auth login を再実行します(有効期限まで 5 分以内であればトークンは自動更新されます) |
| ブートストラップがすでに完了している | eve auth login(既存ユーザー)または eve admin invite(新規ユーザー)を使用します。非本番スタックでは、eve auth bootstrap がサーバーの復旧を自動的に試みます。メールアドレスが間違っている場合の復旧には、eve auth bootstrap --email [email protected] を使用します |
| シークレットが見つからない | eve secrets list で確認し、キーを設定します |
| 補間エラー | ${secret.KEY} の綴りを確認し、eve manifest validate --validate-secrets を実行します |
| Git のクローンに失敗した | github_token または ssh_key のシークレットが設定されていることを確認します |
| サービスが API に接続できない | EVE_API_URL が注入されていることを確認します(eve env show で確認) |
| スコープ付きアクセスが拒否された | eve access explain <permission> <resource> --org <org> を実行して、スコープの一致状況を確認します。バインディングのスコープ制約に対象のパス/スキーマが含まれていることを確認します。組み込みロール(owner/admin/member)にはワイルドカードの envdb スコープがあるため、これらのロールでの envdb の拒否は、スコープ不足ではなく権限セットに原因があります |
| 表示されるロールが間違っている | ロールはライブ DB のメンバーシップから解決されます。有効なロールを確認するには、eve auth permissions を実行します。複数の組織に所属している場合は、eve auth status で組織ごとのメンバーシップ一覧を確認します |
| ジョブで Claude の短期間有効なトークンが使われる | トークンの種類を確認するには、eve auth creds を実行します。setup-token ではなく oauth の場合は、claude setup-token で再生成してから、eve auth sync で再同期します |
| ジョブ間で Codex トークンの有効期限が切れる | 自動書き戻しによって更新されるはずです。更新されない場合は、eve auth sync を再実行します。~/.codex/auth.json または ~/.code/auth.json に新しいトークンがあることを確認します |
| アプリの SSO が機能しない | EVE_SSO_URL が注入されていることを確認します(eve env show)。ローカル開発では、EVE_SSO_URL、EVE_ORG_ID、EVE_API_URL を手動で設定します |
| アプリトークン内の組織メンバーシップが古い | デフォルトの TTL は 1 日です。メンバーシップを即時に確認するには、eveUserAuth() で strategy: 'remote' を使用します |
インシデント対応(シークレット漏えい)
シークレットが漏えいした可能性がある場合:
- 封じ込める:
eve secrets setを使って、ただちにシークレットをローテーションします - 無効化する:影響を受けた環境を再デプロイします
- 監査する:シークレットを使用した最近のジョブを
eve job listで確認します - 復旧する:GitHub、AWS などのソースで新しい認証情報を生成します
- 記録する:インシデントを記録し、ローテーション手順を更新します