Skip to content

app-v1 Issue #3: GitHub App OAuth + NextAuth 登录链路 #164

Closed

Description

@akushonkamen

Background

App v1 Issue #3 — 在 #162 Postgres schema 基础上加 GitHub App OAuth + NextAuth.js 登录链路(PRD docs/prd-app-v1.md §4.1 + §7 Day 0-30 第 4 项 + US-001)。这是后续 webhook (#4) / 仪表盘 (#6) 的鉴权地基 — 没有 session 就无法做 tenant 隔离 (S8)。

GitHub App 已注册(App ID 4331480, Client ID Iv23li3dWlH3ZoRIIKtW, slug gitautodev),5 个凭证已存为 repo secrets:APP_ID, APP_CLIENT_ID, APP_CLIENT_SECRET, APP_PRIVATE_KEY, WEBHOOK_SECRET。本 Issue 只用其中 APP_CLIENT_ID + APP_CLIENT_SECRET。

Context budget paths

允许 AI 读写:

  • app/src/auth/(新建 config.ts + route handler)
  • app/src/app/api/auth/(NextAuth route handler)
  • app/src/app/login/(登录页)
  • app/src/app/dashboard/(最小 dashboard 占位,验证 session 能用)
  • app/src/lib/github-app.ts(GitHub App installation_token helpers,为 feat(#3): Add CONTRIBUTING.md with local-poll setup steps #4 准备)
  • app/src/middleware.ts(受保护路由)
  • app/package.json(加依赖)
  • app/.env.example(加新 env 变量)
  • app/next.config.mjs(如需)
  • docs/app-architecture.md(更新 §4.1 鉴权段引用实际文件)

禁止扫:py/、rust/、.github/workflows/ 非 app-ci、.github/actions/、scripts/。

Files + lines

app/package.json(MODIFY)

  • 新增 deps:
    • next-auth@^4.24.7(已有,确认)
    • @auth/core@^0.37.0(NextAuth v4 新 peer)
    • jose@^5.9.0(JWT 签名/验签 — GitHub App installation_token JWT 用)
  • 不加 prisma / 老的 next-auth v5 beta

app/src/auth/config.ts(NEW)

  • 导出 authOptions: NextAuthOptions
  • provider: GitHubProvider({ clientId, clientSecret, authorization: { params: { scope: 'read:user user:email repo' } } })
  • callback URL 由 NextAuth 自动生成 /api/auth/callback/github
  • adapter: 不用(v1 简化 — 用户表暂用 Postgres tenants 表,但 v1 OAuth 只放 session,不写 tenants 行;那留给 webhook installation 事件 feat(#3): Add CONTRIBUTING.md with local-poll setup steps #4 落表)
  • session strategy: "jwt"(无 session DB 表,简化)
  • jwt secret: process.env.NEXTAUTH_SECRET
  • callbacks:
    • jwt({ token, profile }):profile 是 GitHub user info;token.githubId = profile.id; token.githubLogin = profile.login
    • session({ session, token }):把 githubId/githubLogin 注入 session.user
  • 加 'server-only' 顶部 import

app/src/app/api/auth/[...nextauth]/route.ts(NEW)

  • Next.js App Router 标准写法:
    import NextAuth from "next-auth";
    import { authOptions } from "@/auth/config";
    const handler = NextAuth(authOptions);
    export { handler as GET, handler as POST };

app/src/auth/with-app-installer.ts(NEW,GitHub App helpers)

  • getAppInstallationsForUser(accessToken: string): Promise<{id:number, account:{login:string, type:string}}[]> — 调用 GET /user/installations(GitHub App 用户级 token 才能 list)
  • getAppInstallationToken(installationId: number): Promise<string> — 用 APP_PRIVATE_KEY 签 JWT → POST /app/installations/{id}/access_tokens → 缓存到 50 分钟(S11)
  • signAppJwt(): string — APP_ID + APP_PRIVATE_KEY → RS256 JWT,9 分钟有效期
  • 不实现 installation_events webhook(留给 feat(#3): Add CONTRIBUTING.md with local-poll setup steps #4)

app/src/lib/github-app-jwt.ts(NEW)

  • 用 jose 签 GitHub App JWT:
    import { SignJWT, importPKCS8 } from "jose";
  • 私钥从 process.env.APP_PRIVATE_KEY 读(PEM 文本,不是 base64 — secret 管理用 base64 存储,运行时 base64 decode → PEM)
  • 缓存 importPKCS8 结果避免每次重算

app/src/middleware.ts(NEW)

  • Next.js middleware 保护 /dashboard/* + /settings/*
  • 未登录 → 重定向 /login?callbackUrl=<original>
  • 用 next-auth 的 getToken({ req }) 读 JWT,不查 DB

app/src/app/login/page.tsx(NEW)

  • 简单 client component:一个 "Sign in with GitHub" 按钮 → signIn("github", { callbackUrl: "/dashboard" })
  • 引导文案说明会请求 repo scope

app/src/app/dashboard/page.tsx(NEW,最小占位)

  • 服务端组件,读 getServerSession()
  • 显示 欢迎, {session.user.name} + GitHub ID: {session.user.githubId}
  • 列出该用户的 App installations(调用 getAppInstallationsForUser(session.accessToken))— 没安装则显示 install URL https://github.com/apps/gitautodev/installations/new
  • 不做完整仪表盘 UI(留给 Bug: Login button does not respond to clicks on Safari 18.2 #6)

app/.env.example(MODIFY)

  • 加:
    • NEXTAUTH_URL=http://localhost:3000(开发环境)
    • NEXTAUTH_SECRET=(注释:openssl rand -base64 32)
    • APP_ID=(注释:from GitHub App settings)
    • APP_CLIENT_ID=
    • APP_CLIENT_SECRET=
    • APP_PRIVATE_KEY=(注释:base64-encoded PEM, base64 -i app.private-key.pem)

app/src/app/page.tsx(MODIFY)

  • 把 disabled "Sign in with GitHub" 按钮改成 <Link href="/login"> 或直接 signIn 调用

docs/app-architecture.md(MODIFY)

  • §4.1 鉴权段加:NextAuth GitHubProvider → /api/auth/* → JWT session → middleware 守护 /dashboard
  • 引用实际文件路径 app/src/auth/config.ts

Acceptance criteria

  • pnpm install && pnpm lint && pnpm typecheck && pnpm build 在 app/ 下全部成功(无 NEXTAUTH_SECRET 也能跑 build)
  • /login 页面渲染,有 "Sign in with GitHub" 按钮
  • 点按钮重定向到 GitHub OAuth 授权页(需要 APP_CLIENT_ID/SECRET env 配置后真实跑通,CI 不验证)
  • 回调 /api/auth/callback/github 走通后写入 JWT cookie next-auth.session-token
  • /dashboard 服务端组件能读 session,显示 GitHub login
  • 未登录访问 /dashboard 被中间件拦截重定向 /login?callbackUrl=/dashboard
  • app/src/auth/config.ts 用 next-auth GitHubProvider,scope 含 repo
  • app/src/lib/github-app-jwt.ts 用 jose 签 RS256 JWT,9 分钟有效期
  • app/src/auth/with-app-installer.ts 的 getAppInstallationToken 缓存 token ≤ 50 分钟(S11)
  • 不打印任何 token / secret / cookie 值到日志(S4)
  • .env.example 新增 6 个 env 变量占位(NEXTAUTH_URL/SECRET + APP_ID/CLIENT_ID/CLIENT_SECRET/PRIVATE_KEY)
  • docs/app-architecture.md §4.1 鉴权段引用实际文件路径
  • CI 跑通:app-ci workflow lint + typecheck + build 全绿

Context / non-goals

References

Activity

  1. github-actions commented on Jul 18, 2026

    @github-actions
    Contributor

    Triage: Accepted ✅

    This is a well-specified feature request for app-v1 Issue #3 — GitHub App OAuth + NextAuth login pipeline. The issue has clear context budget paths, per-file specs, and detailed acceptance criteria. No clarification needed.

    Summary of understood scope

    1. Auth config (app/src/auth/config.ts) — NextAuth with GitHubProvider, JWT session strategy, githubId/githubLogin in token+session callbacks
    2. Route handler (app/src/app/api/auth/[...nextauth]/route.ts) — standard App Router catch-all
    3. GitHub App helpers (app/src/auth/with-app-installer.ts, app/src/lib/github-app-jwt.ts) — installation listing, JWT signing (RS256, 9min expiry), token caching (≤50min per S11)
    4. Middleware (app/src/middleware.ts) — protects /dashboard/* + /settings/*, redirects unauthenticated to /login
    5. Login page (app/src/app/login/page.tsx) — "Sign in with GitHub" button with repo scope
    6. Dashboard placeholder (app/src/app/dashboard/page.tsx) — server component showing session + installations list
    7. Env + config (app/.env.example, app/package.json, app/src/app/page.tsx, docs/app-architecture.md) — 6 new env vars, next-auth/jose deps, updated homepage link, architecture doc update

    Non-goals are explicitly called out (no tenants table write, no webhook installation handling, no BYOK, no Stripe, no full dashboard). Session strategy is JWT (no DB adapter). The app/ directory CI (lint + typecheck + build) must pass.

    Moving into the pipeline.

  2. github-actions commented on Jul 18, 2026

    @github-actions
    Contributor

    Triage: Accepted ✅

    This is a well-specified feature request for app-v1 Issue #3 — GitHub App OAuth + NextAuth login pipeline. The issue has clear context budget paths, per-file specs, and detailed acceptance criteria. No clarification needed.

    Summary of understood scope

    1. Auth config (app/src/auth/config.ts) — NextAuth with GitHubProvider, JWT session strategy, githubId/githubLogin in token+session callbacks
    2. Route handler (app/src/app/api/auth/[...nextauth]/route.ts) — standard App Router catch-all
    3. GitHub App helpers (app/src/auth/with-app-installer.ts, app/src/lib/github-app-jwt.ts) — installation listing, JWT signing (RS256, 9min expiry), token caching (≤50min per S11)
    4. Middleware (app/src/middleware.ts) — protects /dashboard/* + /settings/*, redirects unauthenticated to /login
    5. Login page (app/src/app/login/page.tsx) — "Sign in with GitHub" button with repo scope
    6. Dashboard placeholder (app/src/app/dashboard/page.tsx) — server component showing session + installations list
    7. Env + config (app/.env.example, app/package.json, app/src/app/page.tsx, docs/app-architecture.md) — 6 new env vars, next-auth/jose deps, updated homepage link, architecture doc update

    Non-goals are explicitly called out (no tenants table write, no webhook installation handling, no BYOK, no Stripe, no full dashboard). Session strategy is JWT (no DB adapter). The app/ directory CI (lint + typecheck + build) must pass.

    Moving into the pipeline.

  3. added
    accepted-by-claudeClaude self-acceptance after clarify loop (Module 4 trigger, distinct from maintainer accepted)
    on Jul 18, 2026
  4. akushonkamen commented on Jul 18, 2026

    @akushonkamen
    OwnerAuthor

    🤖 Claude self-acceptance (M8 workload=standard). accepted-by-claude applied — Module 4 develop-gate will pick this up. Maintainer may override with rejected.

  5. akushonkamen commented on Jul 18, 2026

    @akushonkamen
    OwnerAuthor

    🚀 Development branch pushed: claude/issue-164-app-v1-issue-3-github-app-oauth-nextauth @ b67caa09a7ff. PR: #167

  6. added
    verifyingModule 5 self-verify active
    verify:retry-1Verify retry 1/3: self-verify failed, re-running Module 4 to fix
    and removed
    verifyingModule 5 self-verify active
    on Jul 18, 2026
  7. akushonkamen commented on Jul 18, 2026

    @akushonkamen
    OwnerAuthor

    🔄 Verify retry 1/3

    Field Value
    Retry 1 of 3
    PR #167
    Prior workflow run https://github.com/akushonkamen/github-auto-dev-scaffold/actions/runs/29646804472
    Retry label verify:retry-1

    code-generate.yml will auto-trigger on the verify:retry-1 label,
    fetch this comment as the prior-verify-report, and re-run Module 4 to
    address the verify failure.

  8. github-actions commented on Jul 18, 2026

    @github-actions
    Contributor

    🤖 AI Initial Review: posted

    Field Value
    PR #167
    Review comment #167 (comment)
    Workflow run https://github.com/akushonkamen/github-auto-dev-scaffold/actions/runs/29646805124

    The AI has posted an initial review as a PR comment. This is an
    advisory review only — AI does NOT approve. A CODEOWNER must
    perform the final human review and approve (PRD §6).

    Next: Module 9 (merge queue) triggers on approved + green checks.

  9. 14 remaining items

  10. added
    verifyingModule 5 self-verify active
    verifiedModule 5 self-verify passed
    and removed
    verifyingModule 5 self-verify active
    on Jul 18, 2026
  11. akushonkamen commented on Jul 18, 2026

    @akushonkamen
    OwnerAuthor

    ✅ Verify passed — Module 6 (test) engaged. PR: #167

  12. github-actions commented on Jul 18, 2026

    @github-actions
    Contributor

    ❌ Test: failed

    Field Value
    PR #167
    Workflow run https://github.com/akushonkamen/github-auto-dev-scaffold/actions/runs/29647916631

    Test framework: NONE

    This project has no test framework installed (no jest/vitest/mocha in devDependencies), no test config (no jest.config.* / vitest.config.), and no test files (.test.*, .spec.) anywhere in the codebase. The app/package.json has no test script.

    Existing CI checks (run as surrogate "tests")

    Check Result
    pnpm lint ✓ PASS
    pnpm typecheck ✓ PASS
    pnpm build ✓ PASS

    These are build-time checks, not acceptance tests — they verify the code compiles and lints, but exercise none of the Issue #3 runtime behaviour (OAuth flow, middleware redirect, JWT signing, session injection, API call caching).

    Acceptance criteria coverage

    # Criterion Coverage
    1 pnpm install && lint && typecheck && build succeed (without NEXTAUTH_SECRET) ✗ no test
    2 /login renders, has "Sign in with GitHub" button ✗ no test
    3 Button redirects to GitHub OAuth authorize page ✗ no test
    4 Callback writes next-auth.session-token JWT cookie ✗ no test
    5 /dashboard server component reads session, shows GitHub login ✗ no test
    6 Unauthenticated /dashboard redirects to /login?callbackUrl= ✗ no test
    7 auth/config.ts uses GitHubProvider with repo scope ✗ no test
    8 github-app-jwt.ts signs RS256 JWT, 9 min expiry ✗ no test
    9 with-app-installer.ts caches token ≤ 50 min (S11) ✗ no test
    10 No token/secret/cookie printed to logs (S4) ✗ no test
    11 .env.example has 6 new env vars ✗ no test
    12 docs/app-architecture.md §4.1 has real file paths ✗ no test
    13 CI app-ci workflow lint+typecheck+build green ✓ (currently passes on existing code)

    Summary

    Zero test coverage for all 13 acceptance criteria. 0/13 covered by automated tests. The project needs at least a test framework (vitest or jest) and unit/integration tests for the NextAuth config, middleware, JWT helper, installation token cache, and page rendering.

  13. added
    test:retry-2Auto-retry 2/3: re-running Module 4 with test feedback
    and removed
    testingModule 6 test active
    verifiedModule 5 self-verify passed
    test:retry-1Auto-retry 1/3: re-running Module 4 with test feedback
    on Jul 18, 2026
  14. akushonkamen commented on Jul 18, 2026

    @akushonkamen
    OwnerAuthor

    🔄 Auto-retry 2/3

    Field Value
    Retry 2 of 3
    Prior workflow run https://github.com/akushonkamen/github-auto-dev-scaffold/actions/runs/29647916631
    Retry label test:retry-2

    Failure summary (prior Module 6 run)

    Test framework: NONE

    This project has no test framework installed (no jest/vitest/mocha in devDependencies), no test config (no jest.config.* / vitest.config.), and no test files (.test.*, .spec.) anywhere in the codebase. The app/package.json has no test script.

    Existing CI checks (run as surrogate "tests")


    code-generate.yml will auto-trigger on the test:retry-2 label,
    fetch this comment as the prior-test-report, and re-run Module 4 to
    fix the failure.

  15. github-actions commented on Jul 18, 2026

    @github-actions
    Contributor

    ⚠️ code-generate failed. See workflow run for details. A maintainer will triage manually.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedAccepted for development (Module 4 trigger)accepted-by-claudeClaude self-acceptance after clarify loop (Module 4 trigger, distinct from maintainer accepted)enhancementNew feature or requestsize:MOne modulestage:failedA module failed; needs maintainer triagetest:retry-2Auto-retry 2/3: re-running Module 4 with test feedbacktriageNewly opened; awaiting triage botverify:retry-1Verify retry 1/3: self-verify failed, re-running Module 4 to fix

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions