mcpField
P
pinme-auth

pinme-auth

github.com/glitternetwork開発3.8k508 回インストール15 週間前に更新
リポジトリ

pinme-auth について

pinme-authは、PinMe Workerに認証を組み込むためのスキルで、PinMeのIdentity PlatformプロキシAPIを呼び出すTypeScriptの手順を示します。メール登録、トークン検証、ユーザー検索に加え、サーバー用認証情報とフロントエンド設定の区別も説明します。

主な機能

  1. 01メールとパスワードでユーザー登録
  2. 02ログイントークンを検証
  3. 03個別ユーザー情報を取得
  4. 04ページネーションでユーザー一覧を取得
  5. 05WorkerとFirebase Authの連携を案内

活用シーン

  1. 01Workerでメール登録フローを実装
  2. 02フロントエンドから受け取ったid_tokenを検証
  3. 03プロジェクトのユーザー情報を取得・一覧表示

インストール

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

$npx skills add https://github.com/glitternetwork/pinme --skill pinme-auth

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

詳細

PinMe Worker 認証 API 統合

PinMe Worker(TypeScript)から PinMe プラットフォームの Identity Platform 認証プロキシ API を呼び出す方法を説明します。

環境変数

// backend/src/worker.ts
export interface Env {
  DB: D1Database;
  API_KEY: string;       // 项目 API Key — 用于所有 auth 接口认证
  PROJECT_NAME: string;  // 项目名 — 所有 auth 接口必须同时传递
  BASE_URL?: string;     // 可选,默认 https://pinme.cloud
}

API_KEY と PROJECT_NAME は、すべての認証インターフェースで必須の認証情報です。どちらも省略できません。


認証方式(すべてのインターフェースで共通)

パラメーター受け渡し方法必須説明
X-API-Keyリクエストヘッダーはいプロジェクト API Key
project_nameQuery パラメーターはいX-API-Key に対応するものと同じプロジェクトである必要があります

サーバーはまず、これら 2 つのフィールドが同じプロジェクトに対応していることを検証し、プロジェクト設定から tenant_id を取得してから、Identity Platform にリクエストを転送します。


共通エラー

シナリオHTTPdata.error
X-API-Key が不足している401X-API-Key header is required
project_name が不足している400project_name is required
API Key とプロジェクトが一致しない401Invalid API key or project name
プロジェクトに認証テナントが設定されていない400Auth service not configured for this project

共通 TypeScript 型

type ApiEnvelope<T> = {
  code: number   // 200=成功,其他=失败
  msg: string    // "ok" | "fail" | "invalid param"
  data: T
}

type ApiErrorData = { error?: string }

type UserInfo = {
  uid: string
  email: string
  display_name: string
  photo_url?: string
  disabled: boolean
  email_verified: boolean
}

API 1: ユーザーの作成

Endpoint: POST {BASE_URL}/api/v1/auth/create_user?project_name={project_name}

メールアドレスとパスワードによる登録専用です。成功するとユーザーが作成され、確認メールが送信されます。失敗した場合は自動的にロールバックされるため、孤立したアカウントは残りません。

作成後、ユーザーはデフォルトで「未確認」状態です。メールの確認リンクをクリックした後でなければ、verify_token による検証は成功しません。

リクエスト本文
{ "email": "[email protected]", "password": "Test@12345678", "display_name": "Alice" }
フィールド型必須
emailstringはい
passwordstringはい
display_namestringいいえ
エラー
シナリオHTTPdata.error
email/password が不足している400email and password are required
上流での作成に失敗した502Failed to create user
確認メールの送信に失敗した500Failed to send verification email. Please try again.
TypeScript の例
async function createAuthUser(
  env: Env,
  payload: { email: string; password: string; display_name?: string }
): Promise<{ user?: UserInfo; error?: string }> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(
    `${baseUrl}/api/v1/auth/create_user?project_name=${encodeURIComponent(env.PROJECT_NAME)}`,
    {
      method: 'POST',
      headers: { 'X-API-Key': env.API_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify(payload),
    }
  );
  const result = await resp.json() as ApiEnvelope<UserInfo | ApiErrorData>;
  if (!resp.ok || result.code !== 200) {
    return { error: (result.data as ApiErrorData)?.error ?? result.msg };
  }
  return { user: result.data as UserInfo };
}

API 2: id_token の検証

Endpoint: POST {BASE_URL}/api/v1/auth/verify_token?project_name={project_name}

フロントエンドでログイン後に取得した id_token を検証します(メールアドレスとパスワード、Google ログインのどちらにも対応しています)。

注意: token が有効でもメールアドレスが未確認の場合は 403 が返され、401 にはなりません。

リクエスト本文
{ "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." }
成功時のレスポンス data
type VerifyTokenData = {
  uid: string
  email?: string
  tenant_id: string
  claims: Record<string, unknown>
}
エラー
シナリオHTTPdata.error
id_token が不足している400id_token is required
token が無効または期限切れ401Invalid or expired token
メールアドレスが未確認403Email not verified. Please check your inbox and verify your email address.
TypeScript の例
async function verifyAuthToken(
  env: Env,
  idToken: string
): Promise<{ uid?: string; email?: string; error?: string; emailNotVerified?: boolean }> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(
    `${baseUrl}/api/v1/auth/verify_token?project_name=${encodeURIComponent(env.PROJECT_NAME)}`,
    {
      method: 'POST',
      headers: { 'X-API-Key': env.API_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ id_token: idToken }),
    }
  );
  const result = await resp.json() as ApiEnvelope<VerifyTokenData | ApiErrorData>;
  if (!resp.ok || result.code !== 200) {
    const error = (result.data as ApiErrorData)?.error ?? result.msg;
    return { error, emailNotVerified: resp.status === 403 };
  }
  const data = result.data as VerifyTokenData;
  return { uid: data.uid, email: data.email };
}

API 3: 単一ユーザーの取得

Endpoint: GET {BASE_URL}/api/v1/auth/user?project_name={project_name}&uid={uid}

エラー
シナリオHTTPdata.error
uid が不足している400uid is required
ユーザーが存在しない404User not found
上流での取得に失敗した502Failed to get user
TypeScript の例
async function getAuthUser(env: Env, uid: string): Promise<{ user?: UserInfo; error?: string }> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const resp = await fetch(
    `${baseUrl}/api/v1/auth/user?project_name=${encodeURIComponent(env.PROJECT_NAME)}&uid=${encodeURIComponent(uid)}`,
    { method: 'GET', headers: { 'X-API-Key': env.API_KEY } }
  );
  const result = await resp.json() as ApiEnvelope<UserInfo | ApiErrorData>;
  if (!resp.ok || result.code !== 200) {
    return { error: (result.data as ApiErrorData)?.error ?? result.msg };
  }
  return { user: result.data as UserInfo };
}

API 4: ユーザー一覧の取得(ページネーション)

Endpoint: GET {BASE_URL}/api/v1/auth/list_users?project_name={project_name}

デフォルトの max_results=100、最大値は 1000 です。next_page_token を使って繰り返しページを取得します。

Query パラメーター
パラメーター必須説明
project_nameはいプロジェクト名
page_tokenいいえページネーションカーソル
max_resultsいいえ1 ページあたりの件数、1–1000
TypeScript の例
async function listAuthUsers(
  env: Env,
  options: { pageToken?: string; maxResults?: number } = {}
): Promise<{ users?: UserInfo[]; nextPageToken?: string; error?: string }> {
  const baseUrl = env.BASE_URL ?? 'https://pinme.cloud';
  const url = new URL('/api/v1/auth/list_users', baseUrl);
  url.searchParams.set('project_name', env.PROJECT_NAME);
  if (options.pageToken) url.searchParams.set('page_token', options.pageToken);
  if (options.maxResults) url.searchParams.set('max_results', String(options.maxResults));

  const resp = await fetch(url.toString(), { method: 'GET', headers: { 'X-API-Key': env.API_KEY } });
  const result = await resp.json() as ApiEnvelope<{ users: UserInfo[]; next_page_token?: string } | ApiErrorData>;
  if (!resp.ok || result.code !== 200) {
    return { error: (result.data as ApiErrorData)?.error ?? result.msg };
  }
  const data = result.data as { users: UserInfo[]; next_page_token?: string };
  return { users: data.users, nextPageToken: data.next_page_token };
}

// 批量遍历所有用户示例
async function* iterAllUsers(env: Env) {
  let pageToken: string | undefined;
  do {
    const { users, nextPageToken, error } = await listAuthUsers(env, { pageToken, maxResults: 1000 });
    if (error) throw new Error(error);
    for (const user of users ?? []) yield user;
    pageToken = nextPageToken;
  } while (pageToken);
}

フロントエンド統合(Firebase Auth)

create_worker のレスポンスには public_client_config が含まれており、フロントエンドではこれを使って Firebase Auth SDK を初期化します。

2 種類の api_key の違い
フィールド用途ブラウザーへの公開
data.api_key本文に記載されたすべてのプロキシインターフェースの呼び出しに使うプロジェクト API Keyできません。Worker/サーバー専用です
data.public_client_config.auth_api_keyフロントエンドのログイン SDK 初期化に使う Firebase Web API Keyできます
public_client_config フィールドの説明
フィールドフロントエンドでの用途
public_client_config.auth_api_keyinitializeApp({ apiKey })
public_client_config.auth_domaininitializeApp({ authDomain })
public_client_config.auth_project_idinitializeApp({ projectId })
public_client_config.tenant_idauth.tenantId = config.tenant_id(必ず設定してください。設定しないと token の所属先が誤ります)
フロントエンドの TypeScript の例
import { initializeApp } from 'firebase/app'
import {
  type Auth,
  getAuth,
  GoogleAuthProvider,
  signInWithEmailAndPassword,
  signInWithPopup,
} from 'firebase/auth'

type PublicClientConfig = {
  tenant_id: string
  auth_api_key: string
  auth_domain: string
  auth_project_id: string
}

export function createProjectAuth(config: PublicClientConfig): Auth {
  const app = initializeApp({
    apiKey: config.auth_api_key,
    authDomain: config.auth_domain,
    projectId: config.auth_project_id,
  })
  const auth = getAuth(app)
  auth.tenantId = config.tenant_id  // 必须设置,确保 token 归属正确租户
  return auth
}

// 邮箱密码登录,返回 id_token
export async function loginWithEmail(auth: Auth, email: string, password: string): Promise<string> {
  const credential = await signInWithEmailAndPassword(auth, email, password)
  return credential.user.getIdToken()
}

// Google 登录,返回 id_token
export async function loginWithGoogle(auth: Auth): Promise<string> {
  const credential = await signInWithPopup(auth, new GoogleAuthProvider())
  return credential.user.getIdToken()
}

// 用法示例
// pinme create 会自动将 public_client_config 写入 frontend/src/utils/config.ts
import { public_client_config } from '../utils/config'

const auth = createProjectAuth(public_client_config)
const idToken = await loginWithGoogle(auth)
// 然后把 idToken 发给自己的 Worker,由 Worker 调用 verify_token

フロントエンドはログインと id_token の取得のみを担当します。プロジェクトの api_key を直接保持しないでください。verify_token は必ず Worker/サーバー経由で呼び出してください。 frontend/src/utils/config.ts は pinme create によって自動生成されるため、手動で作成する必要はありません。


典型的な呼び出しフロー

メールアドレスとパスワードによる登録フロー:

  1. create_user → ユーザーを作成し、確認メールを送信する
  2. ユーザーがメールのリンクをクリックして確認を完了する
  3. フロントエンドでログインし、id_token を取得する
  4. verify_token → token を検証し、uid を取得する
  5. 必要に応じて getAuthUser を呼び出し、ユーザーの完全な情報を取得する

Google ログインフロー:

  1. フロントエンドで Google Sign-In を完了し、id_token を取得する
  2. verify_token → token を検証する(create_user の呼び出しは不要です)

易错点

错误正确做法
只传 X-API-Key,忘记 project_name每个请求都要同时带 X-API-Key header 和 project_name query
verify_token 返回 403 时当 token 失效处理403 = 邮箱未验证,提示用户检查邮箱;401 才是 token 失效
create_user 成功就认为邮箱已验证创建成功只代表验证邮件已发,用户必须点击后才算验证
list_users 只取第一页有 next_page_token 时需继续请求,直到为空
成功判断只看 resp.ok同时判断 resp.ok && result.code === 200

コメント