关于 nodeops-auth
nodeops-auth 是面向现有 Next.js App Router 项目的设置技能,用于接入 NodeOps PKCE OAuth。它检查项目结构并按需创建路由、回调页和环境配置,同时避免覆盖已完成的设置。
主要功能
- 01检测 Next.js 与 App Router 项目
- 02按包管理器安装 OAuth 集成包
- 03创建认证 API 路由与回调页
- 04将 AuthProvider 接入根布局
- 05管理环境变量与忽略规则
- 06检查 Docker 部署配置
适用场景
- 01为现有 Next.js 应用添加 NodeOps 登录
- 02为 OAuth 回调配置重定向页面
- 03为容器部署核对构建时环境变量
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ nodeops-app/ skills --skill nodeops-auth安装后在 Claude Code 中输入/nodeops-auth确认已加载。
详细介绍
NodeOps Auth 设置
为现有的 Next.js(App Router)项目添加 NodeOps PKCE OAuth。
前置条件
开始之前,请确认项目兼容:
- 必须使用 App Router:检查是否存在
app/目录。如果只存在pages/,请停止并告知用户:“此技能仅支持 Next.js App Router。你的项目使用的是 Pages Router。” - 必须使用 Next.js:检查
package.json中的依赖项是否包含next。如果没有,请停止并告知用户这是一个仅适用于 Next.js 的软件包。
幂等性
执行每个步骤前,先检查相应工作是否已完成。跳过已完成的步骤:
- 如果
package.json的依赖项中已包含@nodeops-createos/integration-oauth,则跳过安装。 - 如果
app/api/auth/me/route.ts和app/api/auth/token/route.ts已存在,则跳过路由创建。 - 如果布局中已导入
@nodeops-createos/integration-oauth的AuthProvider,则跳过封装。 - 如果
app/callback/page.tsx(或.jsx)已存在,则跳过回调页面创建。 - 如果
.env.example已存在且包含NODEOPS_,则跳过环境文件创建。
步骤
-
检测项目设置 — 读取
package.json并检查锁文件:- 包管理器:如果存在
pnpm-lock.yaml,则使用 pnpm;如果存在yarn.lock,则使用 yarn;否则使用 npm。 - TypeScript:如果存在
tsconfig.json,则使用.ts/.tsx扩展名;否则使用.js/.jsx。 - 确认
app/目录存在(App Router)。如果只存在pages/,请停止。 - 确认依赖项中包含
next。如果没有,请停止。
- 包管理器:如果存在
-
安装软件包 — 运行相应的安装命令:
- npm:
npm install @nodeops-createos/integration-oauth - pnpm:
pnpm add @nodeops-createos/integration-oauth - yarn:
yarn add @nodeops-createos/integration-oauth - 如果安装失败:检查用户是否可以访问网络,以及是否可以访问注册表。如果持续失败,建议手动运行该命令。
- npm:
-
创建 API 路由 — 创建两个独立的路由文件,并按需创建目录:
app/api/auth/me/route.ts(或.js):export { GET } from '@nodeops-createos/integration-oauth/server/me';app/api/auth/token/route.ts(或.js):export { POST } from '@nodeops-createos/integration-oauth/server/token'; -
使用 AuthProvider 封装布局 — 读取根布局文件。它可能是
app/layout.tsx、app/layout.jsx、app/layout.js或app/layout.ts。找到实际存在的文件。- 在文件顶部添加导入语句:
import { AuthProvider } from '@nodeops-createos/integration-oauth'; - 在
<body>标签中找到{children}。使用<AuthProvider>将其封装:<AuthProvider>{children}</AuthProvider> - 不要在布局中添加 "use client"。
AuthProvider内部已经标记为"use client",从 Server Component 布局中导入也能正常使用。 - 如果布局中已有其他 Provider(例如 ThemeProvider、QueryClientProvider),请将
<AuthProvider>与它们并列嵌套。不要移除或替换现有 Provider。 - 如果
{children}没有直接位于<body>中(例如,它位于包装用的 div 或 fragment 中),请在{children}出现的位置进行封装。 - 如果不存在布局文件,请停止并告知用户先创建根布局。
- 在文件顶部添加导入语句:
-
创建回调页面 — 询问用户:“登录后应将用户重定向到哪里?(默认:/dashboard)”。将用户的回答用作
redirectTo值。然后创建app/callback/page.tsx(或.jsx):"use client"; import { useCallbackHandler } from "@nodeops-createos/integration-oauth"; export default function Callback() { const { loading, error } = useCallbackHandler({ redirectTo: "/dashboard" }); if (loading) return ( <div style={{ display: "flex", justifyContent: "center", alignItems: "center", height: "100vh" }}> <p>Signing in...</p> </div> ); if (error) return <p>Login failed: {error}</p>; return null; } -
创建
.env.example— 在项目根目录创建此文件:# ── Client-side (exposed to browser via NEXT_PUBLIC_ prefix) ────────────────── NEXT_PUBLIC_NODEOPS_AUTH_URL=https://id.nodeops.network/oauth2/auth NEXT_PUBLIC_NODEOPS_CLIENT_ID=your_client_id_here NEXT_PUBLIC_NODEOPS_REDIRECT_URI=http://localhost:3000/callback NEXT_PUBLIC_NODEOPS_SCOPES=offline_access offline openid # ── Server-side only (never sent to browser) ────────────────────────────────── NODEOPS_CLIENT_SECRET=your_client_secret_here NODEOPS_TOKEN_URL=https://id.nodeops.network/oauth2/token NODEOPS_USERINFO_URL=https://autogen-v2-api.nodeops.network/v1/users/me -
创建
.env.local— 如果.env.local不存在,请将.env.example复制到.env.local,并提醒用户使用其 NodeOps 开发者门户中的凭据填写NEXT_PUBLIC_NODEOPS_CLIENT_ID和NODEOPS_CLIENT_SECRET。如果.env.local已存在,则仅追加缺少的NODEOPS_变量,不要覆盖现有值。 -
检查
.gitignore— 读取.gitignore(如果存在)。如果其中未列出.env.local,请将其添加进去,以防泄露机密信息。同时确保已列出.env。如果不存在.gitignore,请创建一个,至少包含:.env .env.local -
Docker/容器部署 — 检查项目根目录中是否存在
Dockerfile。如果存在,请验证它是否正确处理 NodeOps 环境变量;如果没有,请提醒用户。如果用户询问 Docker 或部署,请创建或更新 Dockerfile。重要:Next.js 会在构建时将
NEXT_PUBLIC_*变量嵌入 JS 包中。必须在RUN npm run build之前,将它们设置为 Dockerfile 中的ENV或ARG。仅在运行时设置不起作用 — 包中会包含undefined。这两类变量:
变量 所需时机 原因 NEXT_PUBLIC_NODEOPS_AUTH_URL构建时 内嵌到客户端 JS 包中 NEXT_PUBLIC_NODEOPS_CLIENT_ID构建时 内嵌到客户端 JS 包中 NEXT_PUBLIC_NODEOPS_REDIRECT_URI构建时 内嵌到客户端 JS 包中 NEXT_PUBLIC_NODEOPS_SCOPES构建时 内嵌到客户端 JS 包中 NODEOPS_CLIENT_SECRET仅运行时 API 路由每次请求时读取 NODEOPS_TOKEN_URL仅运行时 API 路由每次请求时读取 NODEOPS_USERINFO_URL仅运行时 API 路由每次请求时读取 CreateOS 注意事项:存在 Dockerfile 时,CreateOS 会使用它。仅使用单阶段 Dockerfile — 在此平台上,包含
COPY --from=builder的多阶段构建会失败。完全移除 Dockerfile,即可改用 CreateOS 默认的安装/运行命令。有关NEXT_PUBLIC_*变量的运行时配置解决方法,请参阅 CreateOS 部分(步骤 10)。Dockerfile 示例(单阶段,兼容 CreateOS):
FROM node:20-alpine WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . # NEXT_PUBLIC_* must be present at build time — they get baked into the JS bundle ARG NEXT_PUBLIC_NODEOPS_AUTH_URL=https://id.nodeops.network/oauth2/auth ARG NEXT_PUBLIC_NODEOPS_CLIENT_ID ARG NEXT_PUBLIC_NODEOPS_REDIRECT_URI ARG NEXT_PUBLIC_NODEOPS_SCOPES=offline_access offline openid ENV NEXT_PUBLIC_NODEOPS_AUTH_URL=$NEXT_PUBLIC_NODEOPS_AUTH_URL ENV NEXT_PUBLIC_NODEOPS_CLIENT_ID=$NEXT_PUBLIC_NODEOPS_CLIENT_ID ENV NEXT_PUBLIC_NODEOPS_REDIRECT_URI=$NEXT_PUBLIC_NODEOPS_REDIRECT_URI ENV NEXT_PUBLIC_NODEOPS_SCOPES=$NEXT_PUBLIC_NODEOPS_SCOPES # Server-side vars — only needed at runtime, read per-request by API routes ENV NODEOPS_CLIENT_SECRET="" ENV NODEOPS_TOKEN_URL=https://id.nodeops.network/oauth2/token ENV NODEOPS_USERINFO_URL=https://autogen-v2-api.nodeops.network/v1/users/me RUN npm run build EXPOSE 3000 CMD ["npm", "start"]同时确保
.dockerignore排除了.env.local:.env .env.local node_modules .next如果项目部署到 Vercel 或类似平台:无需 Dockerfile — 这些平台会在构建前自动注入环境变量。只需提醒用户在其平台的控制面板中添加这些变量。
-
CreateOS/构建后注入的平台 — 存在 Dockerfile 时,CreateOS 会使用它,但会在构建步骤之后注入环境变量。这意味着即使在 Dockerfile 中使用 ARG/ENV,
NEXT_PUBLIC_*变量在构建时仍为undefined。请使用以下运行时配置解决方法,或完全移除 Dockerfile,以改用 CreateOS 默认的安装/运行命令。软件包会自动处理的内容(v1.x+)
如果包中缺少
NEXT_PUBLIC_*变量,来自@nodeops-createos/integration-oauthv1.x+ 的AuthProvider会在运行时自动获取/api/config。无需额外设置 — 只需确保 API 路由存在(见下方步骤 10a)。如果使用较旧的软件包版本,请手动添加:
10a. 创建
app/api/config/route.ts(或.js):import { NextResponse } from 'next/server'; export const dynamic = 'force-dynamic'; export async function GET() { return NextResponse.json({ NEXT_PUBLIC_NODEOPS_AUTH_URL: process.env.NEXT_PUBLIC_NODEOPS_AUTH_URL || 'https://id.nodeops.network/oauth2/auth', NEXT_PUBLIC_NODEOPS_CLIENT_ID: process.env.NEXT_PUBLIC_NODEOPS_CLIENT_ID || '', NEXT_PUBLIC_NODEOPS_REDIRECT_URI: process.env.NEXT_PUBLIC_NODEOPS_REDIRECT_URI || '', NEXT_PUBLIC_NODEOPS_SCOPES: process.env.NEXT_PUBLIC_NODEOPS_SCOPES || 'offline_access offline openid', }); }此路由会在运行时从服务器环境中读取
NEXT_PUBLIC_*变量(此时这些变量确实可用),并通过 JSON 端点将它们提供给客户端。10b. 创建
components/EnvBootstrap.tsx(或.jsx):"use client"; import { useEffect, useState } from "react"; export default function EnvBootstrap({ children }: { children: React.ReactNode }) { const [ready, setReady] = useState( // 如果变量已写入(例如本地开发),则跳过获取 typeof window !== "undefined" && !!process.env.NEXT_PUBLIC_NODEOPS_CLIENT_ID ); useEffect(() => { if (ready) return; fetch("/api/config") .then((r) => r.json()) .then((env) => { // 修补 process.env,让 AuthProvider 能读取这些变量 Object.assign(process.env, env); setReady(true); }) .catch((err) => { console.error("Failed to load runtime config:", err); setReady(true); // 仍然渲染,以显示错误状态 }); }, [ready]); if (!ready) return null; return <>{children}</>; }10c. 更新根布局,用
EnvBootstrap包裹AuthProvider:import { AuthProvider } from '@nodeops-createos/integration-oauth'; import EnvBootstrap from '@/components/EnvBootstrap'; // 在 <body> 内: <EnvBootstrap> <AuthProvider>{children}</AuthProvider> </EnvBootstrap>这样可以确保先从服务端获取环境变量并写入
process.env,然后再由客户端初始化AuthProvider。 -
显示摘要 — 打印清晰的摘要:
- 创建/修改的文件(逐一列出)
- 跳过的文件(如果文件已存在)
- 提醒用户填写 2 个必需的环境变量:
NEXT_PUBLIC_NODEOPS_CLIENT_ID和NODEOPS_CLIENT_SECRET - 如果存在 Dockerfile,提醒用户必须在 Dockerfile 中运行
npm run build之前设置NEXT_PUBLIC_*变量 - 展示如何在任意页面使用这些钩子:
import { useAuth, useUser } from '@nodeops-createos/integration-oauth'; const { isAuthenticated, login, logout, loading, authError } = useAuth(); const { user } = useUser();