增加谷歌登录

This commit is contained in:
sijinhui 2024-10-02 23:43:30 +08:00
parent 47c72bf010
commit d86d5a1951
7 changed files with 123 additions and 33 deletions

View File

@ -9,7 +9,7 @@ export default function LoginByGithub() {
return ( return (
<div <div
className={`group my-2 flex h-10 w-7/12 items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 `} // className={`group my-2 flex h-10 w-1/12 items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 `} //
> >
<button <button
disabled={loading} disabled={loading}
@ -18,7 +18,7 @@ export default function LoginByGithub() {
e.preventDefault(); e.preventDefault();
signIn("github"); signIn("github");
}} }}
className={`bg-transparent hover:bg-transparent mr-40`} className={`bg-transparent hover:bg-transparent`}
> >
{loading ? ( {loading ? (
<LoadingDots color="#A8A29E" /> <LoadingDots color="#A8A29E" />

View File

@ -0,0 +1,52 @@
"use client";
import LoadingDots from "@/app/components/icons/loading-dots";
import { signIn } from "next-auth/react";
import { useState } from "react";
import { GoogleCircleFilled } from "@ant-design/icons";
export default function LoginByGoogle() {
const [loading, setLoading] = useState(false);
return (
<div
className={`group my-2 flex h-10 w-1/12 items-center justify-center space-x-2 rounded-md border border-stone-200 transition-colors duration-75 focus:outline-none dark:border-stone-700 `} //
>
<button
disabled={loading}
onClick={(e) => {
setLoading(true);
e.preventDefault();
signIn("google", { redirect: false }).then((r) => {
console.log("[auth log]", r);
});
}}
className={`bg-transparent hover:bg-transparent`}
>
{loading ? (
<LoadingDots color="#A8A29E" />
) : (
<>
{/*<svg*/}
{/* className="h-6 w-6 text-gray-500 dark:text-white"*/}
{/* aria-hidden="true"*/}
{/* fill="currentColor"*/}
{/* viewBox="0 0 24 24"*/}
{/*>*/}
{/* */}
{/*</svg>*/}
<GoogleCircleFilled
style={{
fontSize: "24px",
color: "rgb(107 114 128/var(--tw-text-opacity)) !import",
}}
/>
{/*<p className="text-sm font-medium text-stone-600 dark:text-stone-400">*/}
{/* Login with GitHub*/}
{/*</p>*/}
</>
)}
</button>
<div className={`w-1`}></div>
</div>
);
}

View File

@ -1,4 +1,5 @@
import LoginByGithub from "./loginByGithub"; import LoginByGithub from "./loginByGithub";
import LoginByGoogle from "./loginByGoogle";
import UserLoginCore from "./user-login-core"; import UserLoginCore from "./user-login-core";
export default function LoginPage() { export default function LoginPage() {
@ -28,6 +29,8 @@ export default function LoginPage() {
{" "} {" "}
</span> </span>
<LoginByGithub /> <LoginByGithub />
<LoginByGoogle />
<div className="w-5/12" />
</div> </div>
</> </>
); );

View File

@ -14,6 +14,7 @@ import { UserOutlined, MailOutlined, LoadingOutlined } from "@ant-design/icons";
import type { FormProps } from "antd"; import type { FormProps } from "antd";
import { SignInOptions } from "next-auth/react"; import { SignInOptions } from "next-auth/react";
import { getSession } from "next-auth/react"; import { getSession } from "next-auth/react";
import { useSearchParams } from "next/navigation";
export default function UserLoginCore() { export default function UserLoginCore() {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -24,6 +25,27 @@ export default function UserLoginCore() {
const [notification, notificationContextHolder] = const [notification, notificationContextHolder] =
notificationModule.useNotification(); notificationModule.useNotification();
const searchParams = useSearchParams();
const error = searchParams.get("error");
useEffect(() => {
switch (error) {
case "AccessDenied":
openNotification("error", {
message: "登录失败",
description: (
<span>
使
<br />
<span style={{ color: "red" }}></span>
</span>
),
});
break;
default:
break;
}
});
const openNotification = (level: string, arms: NotificationArgsProps) => { const openNotification = (level: string, arms: NotificationArgsProps) => {
if (level === "error") { if (level === "error") {
notification.error({ notification.error({
@ -119,6 +141,7 @@ export default function UserLoginCore() {
} }
signIn(loginProvider, signInOptions).then((result) => { signIn(loginProvider, signInOptions).then((result) => {
setLoading(false); setLoading(false);
console.log("[auth log]", result);
if (!result?.error) { if (!result?.error) {
// 如果没有密码,且登录成功了,说明需要设置密码 // 如果没有密码,且登录成功了,说明需要设置密码
let result_url = let result_url =

View File

@ -1,7 +1,8 @@
import {getServerSession, type NextAuthOptions, Theme} from "next-auth"; import {getServerSession, type NextAuthOptions, Theme} from "next-auth";
import GitHubProvider from "next-auth/providers/github"; import Github from "next-auth/providers/github";
import EmailProvider from "next-auth/providers/email"; import Email from "next-auth/providers/email";
import CredentialsProvider from "next-auth/providers/credentials"; import Credentials from "next-auth/providers/credentials";
import Google from "next-auth/providers/google"
import {PrismaAdapter} from "@next-auth/prisma-adapter"; import {PrismaAdapter} from "@next-auth/prisma-adapter";
import prisma from "@/lib/prisma"; import prisma from "@/lib/prisma";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
@ -23,7 +24,7 @@ export const authOptions: NextAuthOptions = {
useSecureCookies: SECURE_COOKIES, useSecureCookies: SECURE_COOKIES,
secret: process.env.NEXTAUTH_SECRET, secret: process.env.NEXTAUTH_SECRET,
providers: [ providers: [
GitHubProvider({ Github({
clientId: process.env.AUTH_GITHUB_ID as string, clientId: process.env.AUTH_GITHUB_ID as string,
clientSecret: process.env.AUTH_GITHUB_SECRET as string, clientSecret: process.env.AUTH_GITHUB_SECRET as string,
profile(profile) { profile(profile) {
@ -37,9 +38,10 @@ export const authOptions: NextAuthOptions = {
}, },
httpOptions: { httpOptions: {
timeout: 50000, timeout: 50000,
} },
allowDangerousEmailAccountLinking: true,
}), }),
EmailProvider({ Email({
server: { server: {
host: process.env.EMAIL_SERVER_HOST, host: process.env.EMAIL_SERVER_HOST,
port: parseInt(process.env.EMAIL_SERVER_PORT ?? "0"), port: parseInt(process.env.EMAIL_SERVER_PORT ?? "0"),
@ -83,7 +85,7 @@ export const authOptions: NextAuthOptions = {
} }
}, },
}), }),
CredentialsProvider({ Credentials({
// The name to display on the sign in form (e.g. "Sign in with...") // The name to display on the sign in form (e.g. "Sign in with...")
name: "Credentials", name: "Credentials",
// `credentials` is used to generate a form on the sign in page. // `credentials` is used to generate a form on the sign in page.
@ -110,7 +112,7 @@ export const authOptions: NextAuthOptions = {
user['name'] = username; user['name'] = username;
} }
// 目前用户不存在,则会创建新用户。 // 目前用户不存在,则会创建新用户。
let existingUser = await existUser(user); // await insertUser(user) let existingUser = await existUser(user);
if (!existingUser) { if (!existingUser) {
user['allowToLogin'] = !!await getSetting("allowNewUser"); user['allowToLogin'] = !!await getSetting("allowNewUser");
existingUser = await insertUser(user); existingUser = await insertUser(user);
@ -125,7 +127,12 @@ export const authOptions: NextAuthOptions = {
// You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter // You can also Reject this callback with an Error thus the user will be sent to the error page with the error message as a query parameter
} }
} }
}) }),
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
allowDangerousEmailAccountLinking: true,
}),
], ],
pages: { pages: {
signIn: `/login`, signIn: `/login`,
@ -180,13 +187,18 @@ export const authOptions: NextAuthOptions = {
}, },
// 过滤不存在的用户 // 过滤不存在的用户
async signIn({ user, account, profile, email, credentials }) { async signIn({ user, account, profile, email, credentials }) {
const existingUser = await existUser(user as User); let existingUser = await existUser(user as User);
if (!existingUser) {
user['allowToLogin'] = !!await getSetting("allowNewUser");
existingUser = await insertUser(user)
}
// console.log('---', user, 'account', account, 'email', email, 'exist', existingUser) // console.log('---', user, 'account', account, 'email', email, 'exist', existingUser)
// 顺便过滤掉不允许登录的用户 // 顺便过滤掉不允许登录的用户
return !!existingUser && existingUser.allowToLogin; return existingUser.allowToLogin;
}, },
// 重定向 // 重定向
async redirect({ url, baseUrl }) { async redirect({ url, baseUrl }) {
console.log('---------', url, new URL(url), baseUrl)
return baseUrl; return baseUrl;
} }
}, },

View File

@ -24,11 +24,11 @@ export default async function middleware(req: NextRequest) {
if (!isAdminUser) return NextResponse.json({error: '无管理员授权'}, { status: 401 }); if (!isAdminUser) return NextResponse.json({error: '无管理员授权'}, { status: 401 });
} }
// 不是用户且页面不是登录页 // 不是用户且页面不是登录页
if (!isUser && path !== "/login" ) { if (!isUser && !path.startsWith("/login") ) {
return NextResponse.redirect(new URL("/login", req.url)); return NextResponse.redirect(new URL("/login", req.url));
} }
// 如果登录了且页面是登录页面 // 如果登录了且页面是登录页面
if (isUser && path == "/login") { if (isUser && path.startsWith("/login")) {
return NextResponse.redirect(new URL("/", req.url)) return NextResponse.redirect(new URL("/", req.url))
} }

View File

@ -31,7 +31,7 @@ model User {
everyLimitToken Int? @default(0) everyLimitToken Int? @default(0)
createdAt DateTime @default(now()) createdAt DateTime @default(now())
updatedAt DateTime @updatedAt updatedAt DateTime @updatedAt
allowToLogin Boolean @default(true) allowToLogin Boolean @default(false)
isAdmin Boolean? @default(false) isAdmin Boolean? @default(false)
accounts Account[] accounts Account[]
sessions Session[] sessions Session[]
@ -46,11 +46,11 @@ model Account {
providerAccountId String providerAccountId String
refresh_token String? refresh_token String?
refresh_token_expires_in Int? refresh_token_expires_in Int?
access_token String? access_token String? @db.VarChar(512)
expires_at Int? expires_at Int?
token_type String? token_type String?
scope String? scope String?
id_token String? id_token String? @db.Text
session_state String? session_state String?
oauth_token_secret String? oauth_token_secret String?
oauth_token String? oauth_token String?