-
Notifications
You must be signed in to change notification settings - Fork 39
完全免费的登录系统 #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
完全免费的登录系统 #111
Changes from 4 commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
3fcf595
feat: 对App Router添加Auth处理,为登录系统做准备
longsizhuo b2c806e
feat:连接GitHub的Auth
longsizhuo b245705
fix: 我现在能看到数据库中有字段了
longsizhuo 563ed48
feat: 添加本地开发者策略,即没有.env的情况
longsizhuo 80cdf29
refactor: 用新的auth,避免不一致
longsizhuo 019e40f
refactor: Update GitHub profile ID handling for compatibility with da…
longsizhuo ab447a8
fix: 打印不会泄露敏感和数据
longsizhuo 83d0f62
feat: 更新 .env.sample 文件,添加环境变量示例以支持项目配置
longsizhuo 556cc79
refactor: Remove unoptimized images configuration from Next.js settings
longsizhuo 93914c6
chore: Add environment variables for authentication and database conf…
longsizhuo ce34a56
fix: 去除pnpm build
longsizhuo File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,5 @@ | ||
| AUTH_URL=http://localhost:3000 | ||
| AUTH_SECRET=replace-with-32-byte-secret | ||
| AUTH_GITHUB_ID=your-github-oauth-client-id | ||
| AUTH_GITHUB_SECRET=your-github-oauth-client-secret | ||
| GITHUB_TOKEN=github_pat_xxxxxxxxxxxxxx |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,4 +54,5 @@ Agents.md | |
| .env | ||
|
|
||
| # VS code | ||
| .vscode | ||
| .vscode | ||
| /generated/prisma | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| # Login Flow Overview | ||
|
|
||
| 这个目录承载站点的 NextAuth 路由处理。下面是登录流程与依赖的简要说明: | ||
|
|
||
| - **身份验证框架**:使用 [NextAuth.js 5 (Auth.js)](https://authjs.dev/) 作为认证核心,通过 `app/auth.ts` 暴露的 `handlers` 响应 GET/POST 请求。 | ||
| - **OAuth provider**:接入 GitHub OAuth(`next-auth/providers/github`),读取用户的 `id/name/email/avatar` 并持久化到本地用户表。 | ||
| - **数据库适配器**:优先使用 [@auth/neon-adapter](https://authjs.dev/reference/adapter/neon) 将用户、账户、会话数据写入 Neon Postgres。若运行环境缺少 `DATABASE_URL`,系统会回退到 JWT 会话策略,不再访问数据库,方便协作者在无 Neon 凭据的情况下开发。 | ||
| - **会话策略**:有 Neon 配置时启用数据库会话(`strategy: "database"`),否则改用默认的 JWT 签名。 | ||
| - **必要环境变量**:`AUTH_SECRET`/`NEXTAUTH_SECRET` 用于签名;`AUTH_GITHUB_ID`、`AUTH_GITHUB_SECRET` 用于 GitHub OAuth;`DATABASE_URL` 控制 Neon 连接(可选)。开发环境缺少这些变量时会给出控制台警告并使用安全兜底逻辑,以保证本地能跑通。 | ||
|
|
||
| ### 本地无 `.env` 的执行策略 | ||
|
|
||
| - 如果 `.env` 没有配置,只要 GitHub 登录仍在,NextAuth 会使用内置的开发密钥和 JWT 会话继续工作,登录流程不会报错。 | ||
| - Neon 数据库适配器会被自动禁用,此时用户信息只保存在 cookie/JWT 中,不会写入 `users / sessions` 表;适合纯前端协作者快速启动项目。 | ||
| - 控制台会输出显式警告,提示缺少密钥或数据库连接,确保真正部署前补齐配置。 | ||
|
|
||
| 如需扩展更多 provider 或调整会话策略,可直接修改 `auth.config.ts` 与 `auth.ts`。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| import { handlers } from "@/auth"; | ||
| export const { GET, POST } = handlers; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| import { signIn } from "@/auth"; | ||
| import { Button } from "@/app/components/ui/button"; | ||
|
|
||
| interface SignInButtonProps { | ||
| className?: string; | ||
| } | ||
|
|
||
| export function SignInButton({ className }: SignInButtonProps) { | ||
| return ( | ||
| <form | ||
| className={className} | ||
| action={async () => { | ||
| "use server"; | ||
| await signIn("github"); | ||
| }} | ||
| > | ||
| <Button type="submit" size="sm" variant="outline"> | ||
| Sign in with GitHub | ||
| </Button> | ||
| </form> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| import { signOut } from "@/auth"; | ||
| import { | ||
| Avatar, | ||
| AvatarFallback, | ||
| AvatarImage, | ||
| } from "@/app/components/ui/avatar"; | ||
|
|
||
| interface UserMenuProps { | ||
| user: { | ||
| name?: string | null; | ||
| email?: string | null; | ||
| image?: string | null; | ||
| }; | ||
| } | ||
|
|
||
| export function UserMenu({ user }: UserMenuProps) { | ||
| const initials = user.name?.[0] ?? user.email?.[0] ?? "?"; | ||
|
|
||
| return ( | ||
| <details className="relative inline-block text-left"> | ||
| <summary | ||
| className="flex cursor-pointer list-none items-center rounded-full border border-border bg-background p-0.5 transition hover:border-primary/60 [&::-webkit-details-marker]:hidden" | ||
| aria-label="Account menu" | ||
| > | ||
| <Avatar className="size-9"> | ||
| {user.image ? ( | ||
| <AvatarImage src={user.image} alt={user.name ?? "User avatar"} /> | ||
| ) : ( | ||
| <AvatarFallback>{initials}</AvatarFallback> | ||
| )} | ||
| </Avatar> | ||
| </summary> | ||
|
|
||
| <div className="absolute right-0 mt-2 w-60 overflow-hidden rounded-md border border-border bg-popover shadow-lg"> | ||
| <div className="border-b border-border bg-muted/40 px-4 py-3"> | ||
| <p className="text-sm font-medium text-foreground"> | ||
| {user.name ?? "Signed in"} | ||
| </p> | ||
| {user.email ? ( | ||
| <p className="text-xs text-muted-foreground" title={user.email}> | ||
| {user.email} | ||
| </p> | ||
| ) : null} | ||
| </div> | ||
|
|
||
| <form | ||
| action={async () => { | ||
| "use server"; | ||
| await signOut(); | ||
| }} | ||
| > | ||
| <button | ||
| type="submit" | ||
| className="w-full px-4 py-2 text-left text-sm text-foreground transition hover:bg-muted" | ||
| > | ||
| Sign out | ||
| </button> | ||
| </form> | ||
| </div> | ||
| </details> | ||
| ); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| import type { NextAuthConfig } from "next-auth"; | ||
| import GitHub from "next-auth/providers/github"; | ||
|
|
||
| // 在本地开发环境允许没有 .env 的协作者运行站点,因此先尝试读取两个常见的密钥变量,缺失时再使用内置的开发兜底值。 | ||
| const envSecret = process.env.AUTH_SECRET ?? process.env.NEXTAUTH_SECRET; | ||
| const secret = | ||
| envSecret ?? | ||
| (process.env.NODE_ENV !== "production" | ||
| ? "__involutionhell_dev_secret__" | ||
| : undefined); | ||
|
|
||
| if (!envSecret && process.env.NODE_ENV !== "production") { | ||
| console.warn( | ||
| "[auth] AUTH_SECRET missing – using development fallback secret", | ||
| ); | ||
| } | ||
|
|
||
| if (!secret) { | ||
| throw new Error("[auth] AUTH_SECRET is required in production environments"); | ||
| } | ||
|
|
||
| export const authConfig = { | ||
| secret, | ||
| pages: { | ||
| signIn: "/login", | ||
| }, | ||
| callbacks: { | ||
| authorized({ auth, request: { nextUrl } }) { | ||
| const isLoggedIn = !!auth?.user; | ||
| const isOnProtectedRoute = nextUrl.pathname.startsWith("/dashboard"); | ||
|
|
||
| if (isOnProtectedRoute) { | ||
| if (isLoggedIn) return true; | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| }, | ||
| async signIn({ user, account, profile }) { | ||
| console.log("[auth] signIn payload", { user, account, profile }); | ||
| return true; | ||
| }, | ||
| async session({ session, token }) { | ||
| console.log("[auth] session payload", { session, token }); | ||
| return session; | ||
| }, | ||
| async jwt({ token, user, account, profile }) { | ||
| console.log("[auth] jwt payload", { token, user, account, profile }); | ||
| return token; | ||
| }, | ||
| }, | ||
| providers: [ | ||
| GitHub({ | ||
| profile(profile) { | ||
| return { | ||
| id: `github-${profile.id}`, | ||
| name: profile.name ?? profile.login, | ||
longsizhuo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| email: profile.email, | ||
| image: profile.avatar_url, | ||
| }; | ||
| }, | ||
| }), | ||
| ], | ||
| } satisfies NextAuthConfig; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| import NextAuth from "next-auth"; | ||
| import { authConfig } from "./auth.config"; | ||
| import GitHub from "next-auth/providers/github"; | ||
| import { Pool } from "@neondatabase/serverless"; | ||
| import NeonAdapter from "@auth/neon-adapter"; | ||
|
|
||
| type NeonAdapterPool = Parameters<typeof NeonAdapter>[0]; | ||
|
|
||
| export const { handlers, auth, signIn, signOut } = NextAuth(() => { | ||
| // Neon 连接只在有数据库配置时启用;本地协作者若没有 `.env`,将回退为纯 JWT 会话,避免直接抛错阻塞开发。 | ||
| const databaseUrl = process.env.DATABASE_URL; | ||
| const adapter = databaseUrl | ||
| ? NeonAdapter( | ||
| new Pool({ | ||
| connectionString: databaseUrl, | ||
| }) as unknown as NeonAdapterPool, | ||
| ) | ||
| : undefined; | ||
|
|
||
| if (!databaseUrl) { | ||
| console.warn("[auth] DATABASE_URL missing – running without Neon adapter"); | ||
| } | ||
|
|
||
| return { | ||
| ...authConfig, | ||
| providers: [ | ||
| GitHub({ | ||
| profile(profile) { | ||
| return { | ||
| id: `github-${profile.id}`, // 让 User.id 直接对应 GitHub ID | ||
| name: profile.name ?? profile.login, | ||
| email: profile.email, | ||
| image: profile.avatar_url, | ||
| }; | ||
| }, | ||
| }), | ||
| ], | ||
| ...(adapter | ||
| ? { | ||
| adapter, | ||
| session: { | ||
| strategy: "database" as const, | ||
| }, | ||
| } | ||
| : { | ||
| session: { | ||
| strategy: "jwt" as const, | ||
| }, | ||
| }), | ||
| }; | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,15 @@ | ||
| import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; | ||
| import { SignInButton } from "@/app/components/SignInButton"; | ||
|
|
||
| export function baseOptions(): BaseLayoutProps { | ||
| return { | ||
| nav: { | ||
| title: "Involution Hell", | ||
| children: ( | ||
| <div className="ms-auto flex justify-end"> | ||
| <SignInButton /> | ||
| </div> | ||
| ), | ||
| }, | ||
| }; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import NextAuth from "next-auth"; | ||
| import { authConfig } from "./auth.config"; | ||
|
|
||
| export default NextAuth(authConfig).auth; | ||
longsizhuo marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| export const config = { | ||
| // https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher | ||
| matcher: ["/((?!api|_next/static|_next/image|.*\\.png$).*)"], | ||
| runtime: "nodejs", | ||
| }; | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P1] Remove sensitive payload logging in auth callbacks
The
signIn,session, andjwtcallbacks log their entire payloads (console.log("[auth] …", { user, account, profile, token })). In production these objects contain OAuth access tokens and user data, so the change will leak credentials and personal information into application logs. Consider gating the logs behind a development flag or removing them before release.Useful? React with 👍 / 👎.