
Pinme Auth
- 434 installs
- 3.7k repo stars
- Updated July 25, 2026
- glitternetwork/pinme
pinme-auth is an agent skill that guides developers to integrate PinMe Identity Platform auth proxy APIs in TypeScript Cloudflare Workers for user signup, token verification, and session setup.
About
pinme-auth is a Glitter Network PinMe agent skill for calling Identity Platform auth proxy APIs from a PinMe Worker written in TypeScript. Every request requires both an X-API-Key header and a matching project_name query parameter against env.API_KEY and env.PROJECT_NAME, with optional BASE_URL defaulting to https://pinme.cloud. The skill documents four endpoints—POST create_user for email/password signup with verification email rollback, POST verify_token for id_token validation including 403 email-not-verified handling, GET user by uid, and paginated GET list_users up to 1000 results per page. Frontend integration uses Firebase Auth public_client_config from pinme create, setting auth.tenantId before signInWithEmailAndPassword or Google signInWithPopup, then sending id_tokens to the Worker for server-side verify_token. Developers reach for pinme-auth when building location-pinning PinMe apps that need secure email, Google, or password auth without exposing project API keys in the browser.
- PinMe auth flow setup
- Token and session handling
- Secure credential storage
- Protected API access patterns
- Identity verification hooks
Pinme Auth by the numbers
- 434 all-time installs (skills.sh)
- Ranked #528 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/glitternetwork/pinme --skill pinme-authAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 434 |
|---|---|
| repo stars | ★ 3.7k |
| Last updated | July 25, 2026 |
| Repository | glitternetwork/pinme ↗ |
How do you integrate PinMe Worker authentication APIs?
Implement PinMe authentication flows, token handling, and secure session setup when building apps that pin or share locations via Glitter Network.
Who is it for?
Developers building PinMe Cloudflare Worker backends that must create users, verify Firebase id_tokens, and query Identity Platform users securely.
Skip if: Non-PinMe projects or generic OAuth implementations—the skill assumes PinMe env vars, D1, and PinMe auth proxy URL shapes.
When should I use this skill?
A PinMe Worker project needs email/password signup, id_token verification, user lookup, or Firebase Auth tenant configuration for Glitter Network apps.
What you get
TypeScript fetch helpers, Env interface, Firebase Auth frontend setup, error-handling matrix, and documented signup or Google login call chains.
- Auth fetch helper functions
- Firebase frontend auth setup
- Error-handling reference table
By the numbers
- Documents 4 Identity Platform auth proxy API endpoints
- list_users supports max_results up to 1000 per page
Files
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_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 类型
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" }| 字段 | 类型 | 必填 |
|---|---|---|
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 示例
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>
}错误
| 场景 | 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 示例
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 示例
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_key | Firebase Web API Key,初始化前端登录 SDK | 可以 |
public_client_config 字段说明
| 字段 | 前端用途 |
|---|---|
public_client_config.auth_api_key | initializeApp({ apiKey }) |
public_client_config.auth_domain | initializeApp({ authDomain }) |
public_client_config.auth_project_id | initializeApp({ projectId }) |
public_client_config.tenant_id | auth.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 |
Related skills
FAQ
What credentials does pinme-auth require?
pinme-auth requires every PinMe auth call to include an X-API-Key header and a project_name query parameter that match the same PinMe project. Worker env needs API_KEY, PROJECT_NAME, and optionally BASE_URL defaulting to https://pinme.cloud.
How does pinme-auth handle unverified emails?
pinme-auth documents that verify_token returns HTTP 403—not 401—when an id_token is valid but the email is unverified, with error text prompting inbox verification before treating the session as authenticated.
Should the browser call PinMe verify_token directly?
No—pinme-auth keeps project API_KEY server-side in the Worker. The frontend uses Firebase Auth public_client_config to obtain id_tokens, then sends them to the Worker which calls verify_token on the auth proxy.