From 86e71c0d90fd71752e636e5907242deb0bbeab04 Mon Sep 17 00:00:00 2001 From: Nevo David Date: Wed, 5 Jun 2024 13:13:36 +0700 Subject: [PATCH] feat: registration provider --- .../auth/providers/github.provider.ts | 2 +- .../auth/providers/google.provider.ts | 74 +++++++++++++++++++ .../auth/providers/providers.factory.ts | 21 +++--- apps/frontend/src/components/auth/login.tsx | 2 +- .../auth/providers/github.provider.tsx | 0 .../auth/providers/google.provider.tsx | 45 +++++++++++ .../frontend/src/components/auth/register.tsx | 7 +- apps/frontend/src/middleware.ts | 5 +- .../src/database/prisma/schema.prisma | 1 + .../src/services/email.service.ts | 2 +- 10 files changed, 143 insertions(+), 16 deletions(-) create mode 100644 apps/backend/src/services/auth/providers/google.provider.ts rename apps/frontend/src/{app => components}/auth/providers/github.provider.tsx (100%) create mode 100644 apps/frontend/src/components/auth/providers/google.provider.tsx diff --git a/apps/backend/src/services/auth/providers/github.provider.ts b/apps/backend/src/services/auth/providers/github.provider.ts index dcfdb669..2d8b95ea 100644 --- a/apps/backend/src/services/auth/providers/github.provider.ts +++ b/apps/backend/src/services/auth/providers/github.provider.ts @@ -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` )}`; } diff --git a/apps/backend/src/services/auth/providers/google.provider.ts b/apps/backend/src/services/auth/providers/google.provider.ts new file mode 100644 index 00000000..d895b45a --- /dev/null +++ b/apps/backend/src/services/auth/providers/google.provider.ts @@ -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, + }; + } +} diff --git a/apps/backend/src/services/auth/providers/providers.factory.ts b/apps/backend/src/services/auth/providers/providers.factory.ts index 96af6adb..61048d50 100644 --- a/apps/backend/src/services/auth/providers/providers.factory.ts +++ b/apps/backend/src/services/auth/providers/providers.factory.ts @@ -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(); } -} \ No newline at end of file + } +} diff --git a/apps/frontend/src/components/auth/login.tsx b/apps/frontend/src/components/auth/login.tsx index 91c0f3a8..5f101a37 100644 --- a/apps/frontend/src/components/auth/login.tsx +++ b/apps/frontend/src/components/auth/login.tsx @@ -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'; diff --git a/apps/frontend/src/app/auth/providers/github.provider.tsx b/apps/frontend/src/components/auth/providers/github.provider.tsx similarity index 100% rename from apps/frontend/src/app/auth/providers/github.provider.tsx rename to apps/frontend/src/components/auth/providers/github.provider.tsx diff --git a/apps/frontend/src/components/auth/providers/google.provider.tsx b/apps/frontend/src/components/auth/providers/google.provider.tsx new file mode 100644 index 00000000..1bbb8a60 --- /dev/null +++ b/apps/frontend/src/components/auth/providers/google.provider.tsx @@ -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 ( +
+
+ + + + + + +
+
Sign in with Google
+
+ ); +}; diff --git a/apps/frontend/src/components/auth/register.tsx b/apps/frontend/src/components/auth/register.tsx index 698b2826..bf8242a2 100644 --- a/apps/frontend/src/components/auth/register.tsx +++ b/apps/frontend/src/components/auth/register.tsx @@ -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 - {!isAfterProvider && !isGeneral() && } - {!isAfterProvider && !isGeneral() && ( + {!isAfterProvider && (!isGeneral() ? : )} + {!isAfterProvider && (
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 / diff --git a/libraries/nestjs-libraries/src/database/prisma/schema.prisma b/libraries/nestjs-libraries/src/database/prisma/schema.prisma index b659ab95..be6310e4 100644 --- a/libraries/nestjs-libraries/src/database/prisma/schema.prisma +++ b/libraries/nestjs-libraries/src/database/prisma/schema.prisma @@ -413,6 +413,7 @@ enum Period { enum Provider { LOCAL GITHUB + GOOGLE } enum Role { diff --git a/libraries/nestjs-libraries/src/services/email.service.ts b/libraries/nestjs-libraries/src/services/email.service.ts index be586c7c..96bc94e4 100644 --- a/libraries/nestjs-libraries/src/services/email.service.ts +++ b/libraries/nestjs-libraries/src/services/email.service.ts @@ -11,7 +11,7 @@ export class EmailService { return; } await resend.emails.send({ - from: 'Gitroom ', + from: process.env.IS_GENERAL === 'true' ? 'Nevo ' : 'Nevo ', to, subject, html,