$npx -y skills add glitternetwork/pinme --skill pinme-authUse when a PinMe project (Worker TypeScript) needs to integrate user authentication — creating email/password users, verifying id_tokens, querying user info, or listing users via Identity Platform auth proxy APIs.
| 1 | # PinMe Worker Auth API Integration |
| 2 | |
| 3 | Guides how to call PinMe platform's Identity Platform auth proxy APIs in a PinMe Worker (TypeScript). |
| 4 | |
| 5 | ## Environment Variables |
| 6 | |
| 7 | ```typescript |
| 8 | // backend/src/worker.ts |
| 9 | export interface Env { |
| 10 | DB: D1Database; |
| 11 | API_KEY: string; // 项目 API Key — 用于所有 auth 接口认证 |
| 12 | PROJECT_NAME: string; // 项目名 — 所有 auth 接口必须同时传递 |
| 13 | BASE_URL?: string; // 可选,默认 https://pinme.cloud |
| 14 | } |
| 15 | ``` |
| 16 | |
| 17 | > `API_KEY` 和 `PROJECT_NAME` 是所有 auth 接口的必填凭证,缺一不可。 |
| 18 | |
| 19 | --- |
| 20 | |
| 21 | ## 认证方式(所有接口通用) |
| 22 | |
| 23 | | 参数 | 传递方式 | 必填 | 说明 | |
| 24 | |------|---------|------|------| |
| 25 | | `X-API-Key` | 请求头 | 是 | 项目 API Key | |
| 26 | | `project_name` | Query 参数 | 是 | 必须与 `X-API-Key` 对应同一个项目 | |
| 27 | |
| 28 | 服务端会先校验这两个字段是否匹配同一个项目,再从项目配置中取出 `tenant_id`,然后转调 Identity Platform。 |
| 29 | |
| 30 | --- |
| 31 | |
| 32 | ## 通用错误 |
| 33 | |
| 34 | | 场景 | HTTP | `data.error` | |
| 35 | |------|------|-------------| |
| 36 | | 缺少 `X-API-Key` | 401 | `X-API-Key header is required` | |
| 37 | | 缺少 `project_name` | 400 | `project_name is required` | |
| 38 | | API Key 和项目不匹配 | 401 | `Invalid API key or project name` | |
| 39 | | 项目未配置认证租户 | 400 | `Auth service not configured for this project` | |
| 40 | |
| 41 | --- |
| 42 | |
| 43 | ## 通用 TypeScript 类型 |
| 44 | |
| 45 | ```typescript |
| 46 | type ApiEnvelope<T> = { |
| 47 | code: number // 200=成功,其他=失败 |
| 48 | msg: string // "ok" | "fail" | "invalid param" |
| 49 | data: T |
| 50 | } |
| 51 | |
| 52 | type ApiErrorData = { error?: string } |
| 53 | |
| 54 | type UserInfo = { |
| 55 | uid: string |
| 56 | email: string |
| 57 | display_name: string |
| 58 | photo_url?: string |
| 59 | disabled: boolean |
| 60 | email_verified: boolean |
| 61 | } |
| 62 | ``` |
| 63 | |
| 64 | --- |
| 65 | |
| 66 | ## API 1: 创建用户 |
| 67 | |
| 68 | **Endpoint:** `POST {BASE_URL}/api/v1/auth/create_user?project_name={project_name}` |
| 69 | |
| 70 | 仅用于邮箱密码注册。成功时用户已创建且验证邮件已发出;失败时自动回滚,不会留下僵尸账号。 |
| 71 | |
| 72 | > 创建成功后用户默认仍是"未验证"状态,需点击邮件验证链接后,`verify_token` 才能通过校验。 |
| 73 | |
| 74 | ### 请求体 |
| 75 | |
| 76 | ```json |
| 77 | { "email": "alice@example.com", "password": "Test@12345678", "display_name": "Alice" } |
| 78 | ``` |
| 79 | |
| 80 | | 字段 | 类型 | 必填 | |
| 81 | |------|------|------| |
| 82 | | `email` | string | 是 | |
| 83 | | `password` | string | 是 | |
| 84 | | `display_name` | string | 否 | |
| 85 | |
| 86 | ### 错误 |
| 87 | |
| 88 | | 场景 | HTTP | `data.error` | |
| 89 | |------|------|-------------| |
| 90 | | 缺少 email/password | 400 | `email and password are required` | |
| 91 | | 上游创建失败 | 502 | `Failed to create user` | |
| 92 | | 发送验证邮件失败 | 500 | `Failed to send verification email. Please try again.` | |
| 93 | |
| 94 | ### TypeScript 示例 |
| 95 | |
| 96 | ```typescript |
| 97 | async function createAuthUser( |
| 98 | env: Env, |
| 99 | payload: { email: string; password: string; display_name?: string } |
| 100 | ): Promise<{ user?: UserInfo; error?: string }> { |
| 101 | const baseUrl = env.BASE_URL ?? 'https://pinme.cloud'; |
| 102 | const resp = await fetch( |
| 103 | `${baseUrl}/api/v1/auth/create_user?project_name=${encodeURIComponent(env.PROJECT_NAME)}`, |
| 104 | { |
| 105 | method: 'POST', |
| 106 | headers: { 'X-API-Key': env.API_KEY, 'Content-Type': 'application/json' }, |
| 107 | body: JSON.stringify(payload), |
| 108 | } |
| 109 | ); |
| 110 | const result = await resp.json() as ApiEnvelope<UserInfo | ApiErrorData>; |
| 111 | if (!resp.ok || result.code !== 200) { |
| 112 | return { error: (result.data as ApiErrorData)?.error ?? result.msg }; |
| 113 | } |
| 114 | return { user: result.data as UserInfo }; |
| 115 | } |
| 116 | ``` |
| 117 | |
| 118 | --- |
| 119 | |
| 120 | ## API 2: 校验 id_token |
| 121 | |
| 122 | **Endpoint:** `POST {BASE_URL}/api/v1/auth/verify_token?project_name={project_name}` |
| 123 | |
| 124 | 校验前端登录后拿到的 `id_token`(邮箱密码或 Google 登录均适用)。 |
| 125 | |
| 126 | **注意:** token 合法但邮箱未验证时返回 `403`,不是 `401`。 |
| 127 | |
| 128 | ### 请求体 |
| 129 | |
| 130 | ```json |
| 131 | { "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6..." } |
| 132 | ``` |
| 133 | |
| 134 | ### 成功响应 data |
| 135 | |
| 136 | ```typescript |
| 137 | type VerifyTokenData = { |
| 138 | uid: string |
| 139 | email?: string |
| 140 | tenant_id: string |
| 141 | claims: Record<string, unknown> |
| 142 | } |
| 143 | ``` |
| 144 | |
| 145 | ### 错误 |
| 146 | |
| 147 | | 场景 | HTTP | `data.error` | |
| 148 | |------|------|-------------| |
| 149 | | 缺少 `id_token` | 400 | `id_token is required` | |
| 150 | | token 无效或过期 | 401 | `Invalid or expired token` | |
| 151 | | 邮箱未验证 | 403 | `Email not verified. Please check your inbox and verify your email address.` | |
| 152 | |
| 153 | ### TypeScript 示例 |
| 154 | |
| 155 | ```typescript |
| 156 | async function verifyAuthToken( |
| 157 | env: Env, |
| 158 | idToken: string |
| 159 | ): Promise<{ uid?: string; email?: string; error?: string; emailNotVerified?: boolean }> { |
| 160 | const baseUrl = env.BASE_URL ?? 'https://pinme.cloud'; |
| 161 | const resp = await fetch( |
| 162 | `${baseUrl}/api/v1/auth/verify_token?project_name=${encodeURIComponent(env.PROJECT_NAME)}`, |
| 163 | { |
| 164 | method: 'POST', |
| 165 | headers: { 'X-API-Key': env.API_KEY, 'Content-Type': 'application/json' }, |
| 166 | body: JSON.stringify({ id_token: idToken }), |
| 167 | } |
| 168 | ); |
| 169 | const result = await resp.json() as ApiEnvelope<VerifyTokenData | ApiErrorData>; |
| 170 | if (!resp.ok || result.code !== 200) { |
| 171 | const error = (result.data as ApiErrorData)?.error ?? result.msg; |
| 172 | return { error, emailNotVerified: resp.status === 403 }; |
| 173 | } |
| 174 | const data = result.data as VerifyTokenData; |
| 175 | return { uid: data.uid, email: data.email }; |
| 176 | } |
| 177 | ``` |
| 178 | |
| 179 | --- |
| 180 | |
| 181 | ## API 3: 查询单个用户 |
| 182 | |
| 183 | **Endpoint:** `GET {BASE_URL}/api/v1/auth/user?project_name={project_name}&uid={uid}` |
| 184 | |
| 185 | ### 错误 |
| 186 | |
| 187 | | 场景 | HTTP | `data.error` | |
| 188 | |------|------|-------------| |
| 189 | | 缺少 `uid` | 400 | `uid is required` | |
| 190 | | 用户不存在 | 404 | `User not found` | |
| 191 | | 上游查询失败 | 502 | `Failed to get user` | |
| 192 | |
| 193 | ### TypeScript 示例 |
| 194 | |
| 195 | `` |