From 919ad1920cf78aba0e867e7cc337ff9a6a9b3e31 Mon Sep 17 00:00:00 2001 From: betich Date: Sun, 6 Aug 2023 02:00:57 +0700 Subject: [PATCH] feat: log in with google --- src/context/AuthContext.tsx | 22 +++++++++++++- src/pages/index.tsx | 9 +++--- src/pages/login/choose.tsx | 29 ++++++++++++++++++ src/pages/{login.tsx => login/index.tsx} | 0 src/pages/oauth/google.tsx | 38 ++++++++++++++++++++++++ src/utils/auth.ts | 28 +++++++++++++++++ 6 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 src/pages/login/choose.tsx rename src/pages/{login.tsx => login/index.tsx} (100%) create mode 100644 src/pages/oauth/google.tsx diff --git a/src/context/AuthContext.tsx b/src/context/AuthContext.tsx index 7fd5bf9..5fbd0e0 100644 --- a/src/context/AuthContext.tsx +++ b/src/context/AuthContext.tsx @@ -21,6 +21,7 @@ export interface IAuthContext { user?: IUser; group?: IGroup; login: () => void; + loginWithGoogle: () => void; logout: () => void; refreshContext: () => Promise; } @@ -81,6 +82,11 @@ const AuthProvider = ({ children }: { children: ReactNode }) => { router.push('/only-107'); } break; + case '/login/choose': + if (isAuthenticated) { + router.push('/'); + } + break; case '/announce-baan': case '/register': case '/wait-baan-selection': @@ -107,6 +113,10 @@ const AuthProvider = ({ children }: { children: ReactNode }) => { window.location.href = `${process.env.NEXT_PUBLIC_SSO_BASE_URL}/login?service=${process.env.NEXT_PUBLIC_APP_BASE_URL}/login`; }, []); + const loginWithGoogle = useCallback(() => { + window.location.href = `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/google`; + }, []); + const logout = useCallback(() => { localStorage.clear(); window.location.href = '/login'; @@ -121,8 +131,18 @@ const AuthProvider = ({ children }: { children: ReactNode }) => { login, logout, refreshContext, + loginWithGoogle, }), - [isReady, isAuthenticated, user, group, login, logout, refreshContext] + [ + isReady, + isAuthenticated, + user, + group, + login, + logout, + refreshContext, + loginWithGoogle, + ] ); return ( diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 9c61624..45d5cfd 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -4,6 +4,7 @@ import Rocket from '@/public/images/rocket.svg'; import RocketSmoke from '@/public/images/rocket-smoke.svg'; import { useAuth } from '@/context/AuthContext'; import { shimmer, toBase64 } from '@/utils/shimmer'; +import Link from 'next/link'; const Home = () => { const { login } = useAuth(); @@ -35,12 +36,12 @@ const Home = () => {

2023

- + เข้าสู่ระบบ + ); }; diff --git a/src/pages/login/choose.tsx b/src/pages/login/choose.tsx new file mode 100644 index 0000000..75ffe7a --- /dev/null +++ b/src/pages/login/choose.tsx @@ -0,0 +1,29 @@ +import { useAuth } from '@/context/AuthContext'; + +export default function LoginChoice() { + const { login, loginWithGoogle } = useAuth(); + + return ( +
+

+ เข้าสู่ระบบ +

+ +
+ + + +
+
+ ); +} diff --git a/src/pages/login.tsx b/src/pages/login/index.tsx similarity index 100% rename from src/pages/login.tsx rename to src/pages/login/index.tsx diff --git a/src/pages/oauth/google.tsx b/src/pages/oauth/google.tsx new file mode 100644 index 0000000..66b37f6 --- /dev/null +++ b/src/pages/oauth/google.tsx @@ -0,0 +1,38 @@ +import { exchangeGoogleCodeForToken } from '@/utils/auth'; +import { httpPost } from '@/utils/axios'; +import { useRouter } from 'next/router'; +import { useCallback, useEffect, useState } from 'react'; + +// example: https://freshmen2023.sgcu.in.th/oauth/google?code=4%2F0Adeu5BXJOzVuq9DUnfXhMaFmv8olktEtZato8IIX6_hvL_s1SWTZdv6Cj9LwJ00qg8duGw&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid&authuser=0&prompt=consent +export default function Google() { + const { query, replace, isReady } = useRouter(); + + const [message, setMessage] = useState('Loading...'); + + const handleLogin = useCallback(async (code: string) => { + try { + const token = await exchangeGoogleCodeForToken(code); + localStorage.setItem('token', JSON.stringify(token)); + window.location.href = '/register'; + } catch { + setMessage('Failed to login.'); + } + }, []); + + useEffect(() => { + if (isReady) { + if (query?.ticket) { + setMessage('Logging in...'); + handleLogin(query.code as string); + } else { + replace('/'); + } + } + }, [query, replace, handleLogin, isReady]); + + return ( +
+

{message}

+
+ ); +} diff --git a/src/utils/auth.ts b/src/utils/auth.ts index adf446e..cf98f63 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -72,4 +72,32 @@ const getAccessToken = async () => { return accessToken; }; +// Google AUth + +export const exchangeGoogleCodeForToken = async ( + code: string +): Promise => { + let res: AxiosResponse; + try { + res = await authClient.post( + `/auth/google?code=${code}`, + {} + ); + } catch (err) { + const error = err as AxiosError; + throw error; + // return error.response?.status ?? null; + } + + const expiresOn = new Date(); + expiresOn.setSeconds(expiresOn.getSeconds() + res.data.expires_in); + return { + accessToken: res.data.access_token, + refreshToken: res.data.refresh_token, + expiresOn, + }; +}; + +// export const renewGoogleAccessToken = async (refreshToken: string) => { + export { exchangeTicketForToken, getAccessToken };