Skip to content
cccskills
FreePublished on GitHub

pinme-auth

Use 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.

The English summary is being prepared. Showing the original description.

Works withFirebase
See how to install

Files included (1)

  • SKILL.md11.6 KB

SKILL.md (original)

Review the instructions the agent will receive before you install.

PinMe Worker Auth API Integration

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

Environment Variables

// 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_nameQuery 参数是必须与 X-API-Key 对应同一个项目

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


通用错误

场景HTTPdata.error
缺少 X-API-Key401X-API-Key header is required
缺少 project_name400project_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": "alice@example.com", "password": "Test@12345678", "display_name": "Alice" }
字段类型必填
emailstring是
passwordstring是
display_namestring否

错误

场景HTTPdata.error
缺少 email/password400email 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_token400id_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
缺少 uid400uid 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–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。

两种 api_key 区分

字段用途是否可暴露到浏览器
data.api_key项目 API Key,调用本文所有代理接口不能,只给 Worker/服务端
data.public_client_config.auth_api_keyFirebase Web API Key,初始化前端登录 SDK可以

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

Reviews

No reviews yet. Tell others what you thought.

Comments

No login needed. Share how it worked for you, ask a question, or note where it did not run.

No comments yet. Be the first to write one.

Comments are published right away. Do not include personal information such as email addresses or phone numbers. Up to 2 URLs.

0 / 1,000 characters

This form is protected by reCAPTCHA, and the Google Privacy Policy and Terms of Service apply.

More skills from this repository

pinme

Free

Use this skill when the user mentions "pinme", or needs to upload files, store to IPFS, create/publish/deploy websites or full-stack services (including frontend pages, backend APIs, database storage, email sending, etc.), or any feature requiring backend database/server support.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

Use this skill when a PinMe project (Worker TypeScript) needs to integrate email sending (send_email). Guides AI to generate correct Worker TS code.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

Use this skill when a PinMe project (Worker TypeScript) needs to call OpenRouter-backed LLM APIs, including models, chat/completions, streaming, or OpenRouter web search. Guides AI to generate correct Worker TS code.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

Use when a PinMe Cloudflare Worker needs R2 object storage, including secure file or image upload, streaming download, metadata lookup, deletion, listing, Range requests, or R2+D1 coordination. Guides AI to use PinMe's automatically injected env.R2 binding without R2 credentials or manual Wrangler configuration.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

Use this skill when the user wants to share, publish, or upload a static result through PinMe, especially by generating a static HTML share page for a PinMe project link, deployed full-stack app, Codex conversation summary, report, file, demo, or any 分享/发布/上传分享页 request that should end with `pinme upload`.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

Use when generating, modifying, or reviewing PinMe Worker (Cloudflare Worker TypeScript) code that accepts payments through UniwebPay — payment links, products/prices, checkout sessions, payment status reads, refunds, subscriptions, or handling UniwebPay webhooks with @uniwebpay/sdk in a PinMe project.

The English summary is being prepared. Showing the original description.

glitternetwork/pinme3.8KUpdated Sep 12, 2026

See all skills by glitternetwork

Report a problem with this skill