Skip to main content
ClaudeWave
Skill3.7k repo starsupdated 8d ago

pinme-auth

The pinme-auth Claude Code skill provides TypeScript integration patterns for calling PinMe platform's Identity Platform authentication proxy APIs within a PinMe Worker. It documents authentication mechanisms, error handling, and API endpoints for creating email/password users, verifying identity tokens, querying user information, and listing users, requiring API key and project name credentials for all requests.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/glitternetwork/pinme /tmp/pinme-auth && cp -r /tmp/pinme-auth/skills/pinme-auth ~/.claude/skills/pinme-auth
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# PinMe Worker Auth API Integration

Guides how to call PinMe platform's Identity Platform auth proxy APIs in a PinMe Worker (TypeScript).

## Environment Variables

```typescript
// 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` 是所有 auth 接口的必填凭证,缺一不可。

---

## 认证方式(所有接口通用)

| 参数 | 传递方式 | 必填 | 说明 |
|------|---------|------|------|
| `X-API-Key` | 请求头 | 是 | 项目 API Key |
| `project_name` | Query 参数 | 是 | 必须与 `X-API-Key` 对应同一个项目 |

服务端会先校验这两个字段是否匹配同一个项目,再从项目配置中取出 `tenant_id`,然后转调 Identity Platform。

---

## 通用错误

| 场景 | HTTP | `data.error` |
|------|------|-------------|
| 缺少 `X-API-Key` | 401 | `X-API-Key header is required` |
| 缺少 `project_name` | 400 | `project_name is required` |
| API Key 和项目不匹配 | 401 | `Invalid API key or project name` |
| 项目未配置认证租户 | 400 | `Auth service not configured for this project` |

---

## 通用 TypeScript 类型

```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` 才能通过校验。

### 请求体

```json
{ "email": "alice@example.com", "password": "Test@12345678", "display_name": "Alice" }
```

| 字段 | 类型 | 必填 |
|------|------|------|
| `email` | string | 是 |
| `password` | string | 是 |
| `display_name` | string | 否 |

### 错误

| 场景 | HTTP | `data.error` |
|------|------|-------------|
| 缺少 email/password | 400 | `email and password are required` |
| 上游创建失败 | 502 | `Failed to create user` |
| 发送验证邮件失败 | 500 | `Failed to send verification email. Please try again.` |

### TypeScript 示例

```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`。

### 请求体

```json
{ "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." }
```

### 成功响应 data

```typescript
type VerifyTokenData = {
  uid: string
  email?: string
  tenant_id: string
  claims: Record<string, unknown>
}
```

### 错误

| 场景 | HTTP | `data.error` |
|------|------|-------------|
| 缺少 `id_token` | 400 | `id_token is required` |
| token 无效或过期 | 401 | `Invalid or expired token` |
| 邮箱未验证 | 403 | `Email not verified. Please check your inbox and verify your email address.` |

### TypeScript 示例

```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}`

### 错误

| 场景 | HTTP | `data.error` |
|------|------|-------------|
| 缺少 `uid` | 400 | `uid is required` |
| 用户不存在 | 404 | `User not found` |
| 上游查询失败 | 502 | `Failed to get user` |

### TypeScript 示例

```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–1000 |

### TypeScript 示例

```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_u