feat: add github auth integrate

This commit is contained in:
moonrailgun 2024-08-03 03:44:43 +08:00
parent 03bc9b5125
commit 7f7c95b11c
6 changed files with 103 additions and 26 deletions

View File

@ -4,6 +4,7 @@ import { useUserStore } from '@/store/user';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { trpc } from '../trpc'; import { trpc } from '../trpc';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { BuiltInProviderType } from '@auth/core/providers';
export function useAuth() { export function useAuth() {
const trpcUtils = trpc.useUtils(); const trpcUtils = trpc.useUtils();
@ -38,6 +39,32 @@ export function useAuth() {
} }
); );
const loginWithOAuth = useEvent(async (provider: BuiltInProviderType) => {
let res: SignInResponse | undefined;
try {
res = await signIn(provider, {
redirect: false,
});
console.log('res', res);
} catch (err) {
toast.error(t('Login failed'));
throw err;
}
if (res?.error) {
toast.error(t('Login failed'));
throw new Error('Login failed');
}
const userInfo = await trpcUtils.user.info.fetch();
if (!userInfo) {
toast.error(t('Can not get current user info'));
throw new Error('Login failed, ');
}
return userInfo;
});
const logout = useEvent(async () => { const logout = useEvent(async () => {
await signOut({ await signOut({
redirect: false, redirect: false,
@ -49,6 +76,7 @@ export function useAuth() {
return { return {
loginWithPassword, loginWithPassword,
loginWithOAuth,
logout, logout,
}; };
} }

View File

@ -6,6 +6,7 @@ const defaultGlobalConfig: AppRouterOutput['global']['config'] = {
allowRegister: false, allowRegister: false,
alphaMode: false, alphaMode: false,
disableAnonymousTelemetry: false, disableAnonymousTelemetry: false,
authProvider: [],
}; };
const callAnonymousTelemetry = once(() => { const callAnonymousTelemetry = once(() => {

View File

@ -1,6 +1,6 @@
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'; import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router';
import { useGlobalConfig } from '@/hooks/useConfig'; import { useGlobalConfig } from '@/hooks/useConfig';
import { Form, Typography } from 'antd'; import { Divider, Form, Typography } from 'antd';
import { useTranslation } from '@i18next-toolkit/react'; import { useTranslation } from '@i18next-toolkit/react';
import { setUserInfo } from '@/store/user'; import { setUserInfo } from '@/store/user';
import { z } from 'zod'; import { z } from 'zod';
@ -8,6 +8,7 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { useAuth } from '@/api/authjs/useAuth'; import { useAuth } from '@/api/authjs/useAuth';
import { useEventWithLoading } from '@/hooks/useEvent'; import { useEventWithLoading } from '@/hooks/useEvent';
import { LuGithub } from 'react-icons/lu';
export const Route = createFileRoute('/login')({ export const Route = createFileRoute('/login')({
validateSearch: z.object({ validateSearch: z.object({
@ -29,7 +30,7 @@ function LoginComponent() {
const { t } = useTranslation(); const { t } = useTranslation();
const search = Route.useSearch(); const search = Route.useSearch();
const { loginWithPassword } = useAuth(); const { loginWithPassword, loginWithOAuth } = useAuth();
const [handleLogin, loading] = useEventWithLoading(async (values: any) => { const [handleLogin, loading] = useEventWithLoading(async (values: any) => {
const userInfo = await loginWithPassword(values.username, values.password); const userInfo = await loginWithPassword(values.username, values.password);
@ -41,7 +42,7 @@ function LoginComponent() {
replace: true, replace: true,
}); });
}); });
const { allowRegister } = useGlobalConfig(); const { allowRegister, authProvider } = useGlobalConfig();
return ( return (
<div className="flex h-full w-full items-center justify-center dark:bg-gray-900"> <div className="flex h-full w-full items-center justify-center dark:bg-gray-900">
@ -96,6 +97,22 @@ function LoginComponent() {
</Form.Item> </Form.Item>
)} )}
</Form> </Form>
{authProvider.length > 0 && (
<>
<Divider>{t('Or')}</Divider>
<div className="flex justify-center">
<Button
variant="secondary"
className="h-12 w-12 p-3"
onClick={() => loginWithOAuth('github')}
>
<LuGithub size={24} />
</Button>
</div>
</>
)}
</div> </div>
</div> </div>
); );

View File

@ -1,6 +1,7 @@
import { Auth, AuthConfig, createActionURL } from '@auth/core'; import { Auth, AuthConfig, createActionURL } from '@auth/core';
import Nodemailer from '@auth/core/providers/nodemailer'; import Nodemailer from '@auth/core/providers/nodemailer';
import Credentials from '@auth/core/providers/credentials'; import Credentials from '@auth/core/providers/credentials';
import Github from '@auth/core/providers/github';
import { env } from '../utils/env.js'; import { env } from '../utils/env.js';
import { prisma } from './_client.js'; import { prisma } from './_client.js';
import type { PrismaClient, Prisma, User } from '@prisma/client'; import type { PrismaClient, Prisma, User } from '@prisma/client';
@ -28,7 +29,8 @@ export interface UserAuthPayload {
export const authConfig: Omit<AuthConfig, 'raw'> = { export const authConfig: Omit<AuthConfig, 'raw'> = {
debug: env.isProd ? false : true, debug: env.isProd ? false : true,
basePath: '/api/auth', basePath: '/api/auth',
providers: [ trustHost: true,
providers: _.compact([
Credentials({ Credentials({
id: 'account', id: 'account',
name: 'Account', name: 'Account',
@ -52,6 +54,7 @@ export const authConfig: Omit<AuthConfig, 'raw'> = {
return toAdapterUser(user); return toAdapterUser(user);
}, },
}), }),
env.auth.provider.includes('email') &&
Nodemailer({ Nodemailer({
id: 'email', id: 'email',
name: 'Email', name: 'Email',
@ -73,7 +76,19 @@ export const authConfig: Omit<AuthConfig, 'raw'> = {
} }
}, },
}), }),
], env.auth.provider.includes('github') &&
Github({
id: 'github',
name: 'Github',
...env.auth.github,
}),
env.auth.provider.includes('google') &&
Github({
id: 'google',
name: 'Google',
...env.auth.google,
}),
]),
adapter: TianjiPrismaAdapter(prisma), adapter: TianjiPrismaAdapter(prisma),
secret: env.auth.secret, secret: env.auth.secret,
session: { session: {

View File

@ -23,6 +23,7 @@ export const globalRouter = router({
alphaMode: z.boolean(), alphaMode: z.boolean(),
disableAnonymousTelemetry: z.boolean(), disableAnonymousTelemetry: z.boolean(),
customTrackerScriptName: z.string().optional(), customTrackerScriptName: z.string().optional(),
authProvider: z.array(z.string()),
}) })
) )
.query(async ({ input }) => { .query(async ({ input }) => {
@ -34,6 +35,7 @@ export const globalRouter = router({
alphaMode: env.alphaMode, alphaMode: env.alphaMode,
disableAnonymousTelemetry: env.disableAnonymousTelemetry, disableAnonymousTelemetry: env.disableAnonymousTelemetry,
customTrackerScriptName: env.customTrackerScriptName, customTrackerScriptName: env.customTrackerScriptName,
authProvider: env.auth.provider,
}; };
}), }),
}); });

View File

@ -1,5 +1,6 @@
import { v1 as uuid } from 'uuid'; import { v1 as uuid } from 'uuid';
import md5 from 'md5'; import md5 from 'md5';
import _ from 'lodash';
const jwtSecret = const jwtSecret =
!process.env.JWT_SECRET || !process.env.JWT_SECRET ||
@ -13,11 +14,24 @@ export const env = {
jwtSecret, jwtSecret,
port: Number(process.env.PORT || 12345), port: Number(process.env.PORT || 12345),
auth: { auth: {
provider: _.compact([
!!process.env.EMAIL_SERVER && 'email',
!!process.env.AUTH_GITHUB_ID && 'github',
!!process.env.AUTH_GOOGLE_ID && 'google',
]),
secret: process.env.AUTH_SECRET || md5(jwtSecret), secret: process.env.AUTH_SECRET || md5(jwtSecret),
email: { email: {
server: process.env.EMAIL_SERVER, server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM, from: process.env.EMAIL_FROM,
}, },
github: {
clientId: process.env.AUTH_GITHUB_ID,
clientSecret: process.env.AUTH_GITHUB_SECRET,
},
google: {
clientId: process.env.AUTH_GOOGLE_ID,
clientSecret: process.env.AUTH_GOOGLE_SECRET,
},
}, },
allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER), allowRegister: checkEnvTrusty(process.env.ALLOW_REGISTER),
allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI ?? 'true'), allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI ?? 'true'),