feat: registration provider

This commit is contained in:
Nevo David 2024-06-05 13:13:36 +07:00
parent 3551516877
commit 86e71c0d90
10 changed files with 143 additions and 16 deletions

View File

@ -5,7 +5,7 @@ export class GithubProvider implements ProvidersInterface {
return `https://github.com/login/oauth/authorize?client_id=${
process.env.GITHUB_CLIENT_ID
}&scope=user:email&redirect_uri=${encodeURIComponent(
`${process.env.FRONTEND_URL}/settings?provider=github`
`${process.env.FRONTEND_URL}/settings`
)}`;
}

View File

@ -0,0 +1,74 @@
import {
AnalyticsData,
AuthTokenDetails,
PostDetails,
PostResponse,
SocialProvider,
} from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface';
import { makeId } from '@gitroom/nestjs-libraries/services/make.is';
import { google } from 'googleapis';
import { OAuth2Client } from 'google-auth-library/build/src/auth/oauth2client';
import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface';
const clientAndYoutube = () => {
const client = new google.auth.OAuth2({
clientId: process.env.YOUTUBE_CLIENT_ID,
clientSecret: process.env.YOUTUBE_CLIENT_SECRET,
redirectUri: `${process.env.FRONTEND_URL}/integrations/social/youtube`,
});
const youtube = (newClient: OAuth2Client) =>
google.youtube({
version: 'v3',
auth: newClient,
});
const youtubeAnalytics = (newClient: OAuth2Client) =>
google.youtubeAnalytics({
version: 'v2',
auth: newClient,
});
const oauth2 = (newClient: OAuth2Client) =>
google.oauth2({
version: 'v2',
auth: newClient,
});
return { client, youtube, oauth2, youtubeAnalytics };
};
export class GoogleProvider implements ProvidersInterface {
generateLink() {
const state = makeId(7);
const { client } = clientAndYoutube();
return client.generateAuthUrl({
access_type: 'online',
prompt: 'consent',
state,
redirect_uri: `${process.env.FRONTEND_URL}/integrations/social/youtube`,
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email',
],
});
}
async getToken(code: string) {
const { client, oauth2 } = clientAndYoutube();
const { tokens } = await client.getToken(code);
return tokens.access_token;
}
async getUser(providerToken: string) {
const { client, oauth2 } = clientAndYoutube();
client.setCredentials({ access_token: providerToken });
const user = oauth2(client);
const { data } = await user.userinfo.get();
return {
id: data.id!,
email: data.email,
};
}
}

View File

@ -1,12 +1,15 @@
import {Provider} from "@prisma/client";
import {GithubProvider} from "@gitroom/backend/services/auth/providers/github.provider";
import {ProvidersInterface} from "@gitroom/backend/services/auth/providers.interface";
import { Provider } from '@prisma/client';
import { GithubProvider } from '@gitroom/backend/services/auth/providers/github.provider';
import { ProvidersInterface } from '@gitroom/backend/services/auth/providers.interface';
import { GoogleProvider } from '@gitroom/backend/services/auth/providers/google.provider';
export class ProvidersFactory {
static loadProvider(provider: Provider): ProvidersInterface {
switch (provider) {
case Provider.GITHUB:
return new GithubProvider();
}
static loadProvider(provider: Provider): ProvidersInterface {
switch (provider) {
case Provider.GITHUB:
return new GithubProvider();
case Provider.GOOGLE:
return new GoogleProvider();
}
}
}
}

View File

@ -8,7 +8,7 @@ import { Input } from '@gitroom/react/form/input';
import { useMemo, useState } from 'react';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { LoginUserDto } from '@gitroom/nestjs-libraries/dtos/auth/login.user.dto';
import { GithubProvider } from '@gitroom/frontend/app/auth/providers/github.provider';
import { GithubProvider } from '@gitroom/frontend/components/auth/providers/github.provider';
import interClass from '@gitroom/react/helpers/inter.font';
import { isGeneral } from '@gitroom/react/helpers/is.general';

View File

@ -0,0 +1,45 @@
import { useCallback } from 'react';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import interClass from '@gitroom/react/helpers/inter.font';
export const GoogleProvider = () => {
const fetch = useFetch();
const gotoLogin = useCallback(async () => {
const link = await (await fetch('/auth/oauth/GOOGLE')).text();
window.location.href = link;
}, []);
return (
<div
onClick={gotoLogin}
className={`cursor-pointer bg-white h-[44px] rounded-[4px] flex justify-center items-center text-[#121A2D] ${interClass} gap-[4px]`}
>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 48 48"
width="21px"
height="21px"
>
<path
fill="#FFC107"
d="M43.611,20.083H42V20H24v8h11.303c-1.649,4.657-6.08,8-11.303,8c-6.627,0-12-5.373-12-12c0-6.627,5.373-12,12-12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C12.955,4,4,12.955,4,24c0,11.045,8.955,20,20,20c11.045,0,20-8.955,20-20C44,22.659,43.862,21.35,43.611,20.083z"
/>
<path
fill="#FF3D00"
d="M6.306,14.691l6.571,4.819C14.655,15.108,18.961,12,24,12c3.059,0,5.842,1.154,7.961,3.039l5.657-5.657C34.046,6.053,29.268,4,24,4C16.318,4,9.656,8.337,6.306,14.691z"
/>
<path
fill="#4CAF50"
d="M24,44c5.166,0,9.86-1.977,13.409-5.192l-6.19-5.238C29.211,35.091,26.715,36,24,36c-5.202,0-9.619-3.317-11.283-7.946l-6.522,5.025C9.505,39.556,16.227,44,24,44z"
/>
<path
fill="#1976D2"
d="M43.611,20.083H42V20H24v8h11.303c-0.792,2.237-2.231,4.166-4.087,5.571c0.001-0.001,0.002-0.001,0.003-0.002l6.19,5.238C36.971,39.205,44,34,44,24C44,22.659,43.862,21.35,43.611,20.083z"
/>
</svg>
</div>
<div>Sign in with Google</div>
</div>
);
};

View File

@ -8,12 +8,13 @@ import { Input } from '@gitroom/react/form/input';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { classValidatorResolver } from '@hookform/resolvers/class-validator';
import { CreateOrgUserDto } from '@gitroom/nestjs-libraries/dtos/auth/create.org.user.dto';
import { GithubProvider } from '@gitroom/frontend/app/auth/providers/github.provider';
import { GithubProvider } from '@gitroom/frontend/components/auth/providers/github.provider';
import { useSearchParams } from 'next/navigation';
import { LoadingComponent } from '@gitroom/frontend/components/layout/loading';
import interClass from '@gitroom/react/helpers/inter.font';
import { isGeneral } from '@gitroom/react/helpers/is.general';
import clsx from 'clsx';
import { GoogleProvider } from '@gitroom/frontend/components/auth/providers/google.provider';
type Inputs = {
email: string;
@ -125,8 +126,8 @@ export function RegisterAfter({
Sign Up
</h1>
</div>
{!isAfterProvider && !isGeneral() && <GithubProvider />}
{!isAfterProvider && !isGeneral() && (
{!isAfterProvider && (!isGeneral() ? <GithubProvider /> : <GoogleProvider />)}
{!isAfterProvider && (
<div className="h-[20px] mb-[24px] mt-[24px] relative">
<div className="absolute w-full h-[1px] bg-[#28344F] top-[50%] -translate-y-[50%]" />
<div

View File

@ -30,7 +30,10 @@ export async function middleware(request: NextRequest) {
const url = new URL(nextUrl).search;
if (nextUrl.href.indexOf('/auth') === -1 && !authCookie) {
return NextResponse.redirect(new URL(`/auth${url}`, nextUrl.href));
const providers = ['google', 'github'];
const findIndex = providers.find(p => nextUrl.href.indexOf(p) > -1);
const additional = !findIndex ? '' : (url.indexOf('?') > -1 ? '&' : '?') + `provider=${findIndex.toUpperCase()}`;
return NextResponse.redirect(new URL(`/auth${url}${additional}`, nextUrl.href));
}
// If the url is /auth and the cookie exists, redirect to /

View File

@ -413,6 +413,7 @@ enum Period {
enum Provider {
LOCAL
GITHUB
GOOGLE
}
enum Role {

View File

@ -11,7 +11,7 @@ export class EmailService {
return;
}
await resend.emails.send({
from: 'Gitroom <no-reply@gitroom.com>',
from: process.env.IS_GENERAL === 'true' ? 'Nevo <nevo@postiz.com>' : 'Nevo <nevo@gitroom.com>',
to,
subject,
html,