IWish Auth开发者文档
V1 Alpha GitHub

Next.js

使用 App Router 和 TypeScript SDK 接入

Next.js 接入

Next.js App Router 必须在 Route Handler 或 Server Component 的服务端边界接入 IWish Auth。Client Secret、PKCE verifier 和 opaque App Session 不进入客户端组件。

配置

import { createIWishAuth } from "@iwish/auth-sdk/next";

export const auth = createIWishAuth({
  apiUrl: process.env.IWISH_AUTH_API_URL!,
  portalUrl: process.env.IWISH_AUTH_PORTAL_URL!,
  appKey: "reporting",
  clientId: process.env.IWISH_AUTH_CLIENT_ID!,
  clientSecret: process.env.IWISH_AUTH_CLIENT_SECRET!,
  redirectUri: "https://reporting-staging.example.com/auth/callback"
});

分别创建 login、callback 和 logout Route Handler,直接返回 beginLoginhandleCallbacklogout 的 Response。SDK 自动使用 PKCE,并把 App Session 写入 HttpOnlySameSite=Lax Cookie;HTTPS 环境同时启用 Secure

受保护接口

export async function GET(request: Request) {
  const session = await auth.requireAppAccess(request);
  const member = await db.appMember.findUnique({ where: { iwishAuthUserId: session.user.id } });
  if (!member || member.status !== "active") {
    return Response.json({ error: "local_member_required" }, { status: 403 });
  }
  const allowed = await localPermissions.has(member.id, "report.read");
  if (!allowed) return Response.json({ error: "local_permission_denied" }, { status: 403 });
  return Response.json(await loadReports({ member, clientAssignments: session.clientAssignments }));
}

requireAppAccess 验证 session、当前 App、组织开通和 Auth 生命周期;本地成员与 report.read 由业务 App 自己检查。不要使用已返回 410 的 requirePermission

Server Component

Server Component 可以通过受保护的服务端函数读取 session,但不要把 opaque token、Client Secret 或完整员工敏感字段传给 Client Component。只序列化页面确实需要的展示字段。缓存 key 必须至少包含 iwish_auth_user_id、App、本地授权版本和必要的数据范围;收到停用或客户团队 Webhook 后清理相关缓存。

验收

验证 callback 精确匹配、跨 App cookie 被拒绝、无本地成员 403、本地角色不足 403、assignment 不自动授权、飞书离职后旧 session 失效、外部邮箱账号仍需本地成员。production 不得配置通配 callback、宽泛 Cookie Domain 或浏览器可读 session cookie。