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 { trpc } from '../trpc';
import { useTranslation } from '@i18next-toolkit/react';
import { BuiltInProviderType } from '@auth/core/providers';
export function useAuth() {
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 () => {
await signOut({
redirect: false,
@ -49,6 +76,7 @@ export function useAuth() {
return {
loginWithPassword,
loginWithOAuth,
logout,
};
}

View File

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

View File

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

View File

@ -1,6 +1,7 @@
import { Auth, AuthConfig, createActionURL } from '@auth/core';
import Nodemailer from '@auth/core/providers/nodemailer';
import Credentials from '@auth/core/providers/credentials';
import Github from '@auth/core/providers/github';
import { env } from '../utils/env.js';
import { prisma } from './_client.js';
import type { PrismaClient, Prisma, User } from '@prisma/client';
@ -28,7 +29,8 @@ export interface UserAuthPayload {
export const authConfig: Omit<AuthConfig, 'raw'> = {
debug: env.isProd ? false : true,
basePath: '/api/auth',
providers: [
trustHost: true,
providers: _.compact([
Credentials({
id: 'account',
name: 'Account',
@ -52,28 +54,41 @@ export const authConfig: Omit<AuthConfig, 'raw'> = {
return toAdapterUser(user);
},
}),
Nodemailer({
id: 'email',
name: 'Email',
...env.auth.email,
async sendVerificationRequest(params) {
const { identifier, url, provider, theme } = params;
const { host } = new URL(url);
const transport = createTransport(provider.server);
const result = await transport.sendMail({
to: identifier,
from: provider.from,
subject: `Sign in Tianji to ${host}`,
text: `Sign in Tianji to ${host}\n${url}\n\n`,
html: nodemailHtmlBody({ url, host, theme }),
});
const failed = result.rejected.concat(result.pending).filter(Boolean);
if (failed.length) {
throw new Error(`Email (${failed.join(', ')}) could not be sent`);
}
},
}),
],
env.auth.provider.includes('email') &&
Nodemailer({
id: 'email',
name: 'Email',
...env.auth.email,
async sendVerificationRequest(params) {
const { identifier, url, provider, theme } = params;
const { host } = new URL(url);
const transport = createTransport(provider.server);
const result = await transport.sendMail({
to: identifier,
from: provider.from,
subject: `Sign in Tianji to ${host}`,
text: `Sign in Tianji to ${host}\n${url}\n\n`,
html: nodemailHtmlBody({ url, host, theme }),
});
const failed = result.rejected.concat(result.pending).filter(Boolean);
if (failed.length) {
throw new Error(`Email (${failed.join(', ')}) could not be sent`);
}
},
}),
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),
secret: env.auth.secret,
session: {

View File

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

View File

@ -1,5 +1,6 @@
import { v1 as uuid } from 'uuid';
import md5 from 'md5';
import _ from 'lodash';
const jwtSecret =
!process.env.JWT_SECRET ||
@ -13,11 +14,24 @@ export const env = {
jwtSecret,
port: Number(process.env.PORT || 12345),
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),
email: {
server: process.env.EMAIL_SERVER,
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),
allowOpenapi: checkEnvTrusty(process.env.ALLOW_OPENAPI ?? 'true'),