Merge pull request #535 from sheensantoscapadngan/feature/google-signin-signup-integration

Feature/google signin signup integration
This commit is contained in:
vmatsiiako
2023-06-02 23:44:45 -07:00
committed by GitHub
67 changed files with 7542 additions and 7797 deletions
+5 -3
View File
@@ -57,9 +57,11 @@ const App = ({ Component, pageProps, ...appProps }: NextAppProp): JSX.Element =>
) {
return (
<QueryClientProvider client={queryClient}>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
<NotificationProvider>
<AuthProvider>
<Component {...pageProps} />
</AuthProvider>
</NotificationProvider>
</QueryClientProvider>
);
}
@@ -8,6 +8,7 @@ interface Props {
protectedKey: string;
protectedKeyIV: string;
protectedKeyTag: string;
providerAuthToken?: string;
publicKey: string;
encryptedPrivateKey: string;
encryptedPrivateKeyIV: string;
@@ -15,6 +16,7 @@ interface Props {
organizationName: string;
salt: string;
verifier: string;
attributionSource?: string;
}
/**
@@ -49,9 +51,11 @@ const completeAccountInformationSignup = async ({
encryptedPrivateKeyTag,
salt,
verifier,
organizationName
organizationName,
providerAuthToken,
attributionSource
}: Props) => {
const { data } = await apiRequest.post('/api/v2/signup/complete-account/signup', {
const { data } = await apiRequest.post('/api/v3/signup/complete-account/signup', {
email,
firstName,
lastName,
@@ -64,7 +68,9 @@ const completeAccountInformationSignup = async ({
encryptedPrivateKeyTag,
salt,
verifier,
organizationName
organizationName,
providerAuthToken,
attributionSource,
});
return data;
+7 -6
View File
@@ -9,16 +9,17 @@ interface Login1 {
* @param {*} clientPublicKey
* @returns
*/
const login1 = async (email: string, clientPublicKey: string) => {
const response = await fetch("/api/v2/auth/login1", {
const login1 = async (loginDetails: {
email: string;
clientPublicKey: string;
providerAuthToken?: string;
}) => {
const response = await fetch("/api/v3/auth/login1", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
clientPublicKey,
}),
body: JSON.stringify(loginDetails),
});
// need precise error handling about the status code
if (response?.status === 200) {
+7 -6
View File
@@ -17,16 +17,17 @@ interface Login2Response {
* @param {*} clientPublicKey
* @returns
*/
const login2 = async (email: string, clientProof: string) => {
const response = await fetch('/api/v2/auth/login2', {
const login2 = async (loginDetails: {
email: string;
clientProof: string;
providerAuthToken?: string;
}) => {
const response = await fetch('/api/v3/auth/login2', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
email,
clientProof
}),
body: JSON.stringify(loginDetails),
credentials: 'include'
});
// need precise error handling about the status code
+66 -30
View File
@@ -5,9 +5,12 @@ import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import ListBox from '@app/components/basic/Listbox';
// import ListBox from '@app/components/basic/Listbox';
import InitialLoginStep from '@app/components/login/InitialLoginStep';
import LoginStep from '@app/components/login/LoginStep';
import MFAStep from '@app/components/login/MFAStep';
import PasswordInputStep from '@app/components/login/PasswordInputStep';
import { useProviderAuth } from '@app/hooks/useProviderAuth';
import { isLoggedIn } from '@app/reactQuery';
import getWorkspaces from './api/workspace/getWorkspaces';
@@ -18,12 +21,23 @@ export default function Login() {
const [password, setPassword] = useState('');
const [step, setStep] = useState(1);
const { t } = useTranslation();
const lang = router.locale ?? 'en';
// const lang = router.locale ?? 'en';
const [isLoginWithEmail, setIsLoginWithEmail] = useState(false);
const {
providerAuthToken,
email: providerEmail,
setProviderAuthToken,
isProviderUserCompleted
} = useProviderAuth();
const setLanguage = async (to: string) => {
router.push('/login', '/login', { locale: to });
localStorage.setItem('lang', to);
};
if (providerAuthToken && isProviderUserCompleted === false) {
router.push(`/signup?providerAuthToken=${encodeURIComponent(providerAuthToken)}`);
}
// const setLanguage = async (to: string) => {
// router.push('/login', '/login', { locale: to });
// localStorage.setItem('lang', to);
// };
useEffect(() => {
// TODO(akhilmhdh): workspace will be controlled by a workspace context
@@ -42,29 +56,51 @@ export default function Login() {
}
}, []);
const renderStep = (loginStep: number) => {
// TODO: add MFA step
switch (loginStep) {
case 1:
return (
<LoginStep
email={email}
setEmail={setEmail}
password={password}
setPassword={setPassword}
setStep={setStep}
/>
);
case 2:
// TODO: add MFA step
return <MFAStep email={email} password={password} />;
default:
return <div />;
const renderView = (loginStep: number) => {
if (providerAuthToken && step === 1) {
return (
<PasswordInputStep
email={providerEmail}
password={password}
providerAuthToken={providerAuthToken}
setPassword={setPassword}
setProviderAuthToken={setProviderAuthToken}
setStep={setStep}
/>
);
}
if (isLoginWithEmail && loginStep === 1) {
return (
<LoginStep
email={email}
setEmail={setEmail}
password={password}
setPassword={setPassword}
setStep={setStep}
/>
);
}
if (!isLoginWithEmail && loginStep === 1) {
return <InitialLoginStep setIsLoginWithEmail={setIsLoginWithEmail} />;
}
if (step === 2) {
return (
<MFAStep
email={email || providerEmail}
password={password}
providerAuthToken={providerAuthToken}
/>
);
}
return <div />;
};
return (
<div className="flex h-screen flex-col justify-start bg-bunker-800 px-6">
<div className="flex h-screen flex-col justify-center bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700 px-6 pb-28 ">
<Head>
<title>{t('common.head-title', { title: t('login.title') })}</title>
<link rel="icon" href="/infisical.ico" />
@@ -73,12 +109,12 @@ export default function Login() {
<meta name="og:description" content={t('login.og-description') ?? ''} />
</Head>
<Link href="/">
<div className="mb-8 mt-20 flex cursor-pointer justify-center">
<Image src="/images/biglogo.png" height={90} width={120} alt="long logo" />
<div className="mb-4 mt-20 flex justify-center">
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical logo" />
</div>
</Link>
{renderStep(step)}
<div className="absolute right-4 top-0 mt-4 flex items-center justify-center">
{renderView(step)}
{/* <div className="absolute right-4 top-0 mt-4 flex items-center justify-center">
<div className="mx-auto w-48">
<ListBox
isSelected={lang}
@@ -88,7 +124,7 @@ export default function Login() {
text={`${t('common.language')}: `}
/>
</div>
</div>
</div> */}
</div>
);
}
@@ -0,0 +1,10 @@
import { useEffect } from "react";
export default function LoginProviderError() {
useEffect(() => {
window.localStorage.setItem('PROVIDER_AUTH_ERROR', 'err');
window.close();
}, [])
return <div />
}
@@ -0,0 +1,16 @@
import { useEffect } from "react";
import { useRouter } from "next/router"
import SecurityClient from '@app/components/utilities/SecurityClient';
export default function LoginProviderSuccess() {
const router = useRouter();
useEffect(() => {
const { token } = router.query;
SecurityClient.setProviderAuthToken(token as string);
window.close();
}, [])
return <div />
}
+90
View File
@@ -0,0 +1,90 @@
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import { Button, Input } from '@app/components/v2';
import { isLoggedIn } from '@app/reactQuery';
import getWorkspaces from './api/workspace/getWorkspaces';
export default function Login() {
const router = useRouter();
const [password, setPassword] = useState('');
const { t } = useTranslation();
useEffect(() => {
// TODO(akhilmhdh): workspace will be controlled by a workspace context
const redirectToDashboard = async () => {
let userWorkspace;
try {
const userWorkspaces = await getWorkspaces();
userWorkspace = userWorkspaces[0] && userWorkspaces[0]._id;
router.push(`/dashboard/${userWorkspace}`);
} catch (error) {
console.log('Error - Not logged in yet');
}
};
if (isLoggedIn()) {
redirectToDashboard();
}
}, []);
return (
<div className="flex h-screen flex-col justify-center bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700 px-6 pb-28 ">
<Head>
<title>{t('common.head-title', { title: t('login.title') })}</title>
<link rel="icon" href="/infisical.ico" />
<meta property="og:image" content="/images/message.png" />
<meta property="og:title" content={t('login.og-title') ?? ''} />
<meta name="og:description" content={t('login.og-description') ?? ''} />
</Head>
<Link href="/">
<div className="mb-4 mt-20 flex justify-center">
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical logo" />
</div>
</Link>
<div className="mx-auto w-full max-w-md px-6">
<p className="mx-auto mb-6 flex w-max justify-center text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8">
What’s your email?
</p>
<div className="relative flex items-center justify-center lg:w-1/6 w-1/4 min-w-[22rem] mx-auto w-full rounded-lg max-h-24 md:max-h-28">
<div className="flex items-center justify-center w-full rounded-lg max-h-24 md:max-h-28">
<Input
value={password}
onChange={(e) => setPassword(e.target.value)}
type="password"
placeholder="Enter your email..."
isRequired
autoComplete="email"
id="email"
className="h-12"
/>
</div>
</div>
<div className='lg:w-1/6 w-1/4 w-full mx-auto flex items-center justify-center min-w-[22rem] text-center rounded-md mt-4'>
<Button
colorSchema="primary"
variant="outline_bg"
onClick={() => {}}
isFullWidth
className="h-14"
>
{t('login.login')}
</Button>
</div>
<div className="flex flex-row items-center justify-center mt-4">
<button
onClick={() => {router.push('/login')}}
type="button"
className="text-bunker-300 text-sm hover:underline mt-2 hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer"
>
{t('login.other-option')}
</button>
</div>
</div>
</div>
);
}
+79 -45
View File
@@ -3,16 +3,17 @@ import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import Head from 'next/head';
import Image from 'next/image';
import Link from 'next/link';
import { useRouter } from 'next/router';
import CodeInputStep from '@app/components/signup/CodeInputStep';
import DownloadBackupPDF from '@app/components/signup/DonwloadBackupPDFStep';
import EnterEmailStep from '@app/components/signup/EnterEmailStep';
import InitialSignupStep from '@app/components/signup/InitialSignupStep';
import TeamInviteStep from '@app/components/signup/TeamInviteStep';
import UserInfoStep from '@app/components/signup/UserInfoStep';
import SecurityClient from '@app/components/utilities/SecurityClient';
import { useFetchServerStatus } from '@app/hooks/api/serverDetails';
import { useProviderAuth } from '@app/hooks/useProviderAuth';
import checkEmailVerificationCode from './api/auth/CheckEmailVerificationCode';
import getWorkspaces from './api/workspace/getWorkspaces';
@@ -23,15 +24,26 @@ import getWorkspaces from './api/workspace/getWorkspaces';
export default function SignUp() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
const [name, setName] = useState('');
const [organizationName, setOrganizationName] = useState('');
const [attributionSource, setAttributionSource] = useState('');
const [code, setCode] = useState('123456');
const [codeError, setCodeError] = useState(false);
const [step, setStep] = useState(1);
const router = useRouter();
const { data: serverDetails } = useFetchServerStatus();
const [isSignupWithEmail, setIsSignupWithEmail] = useState(false);
const [isCodeInputCheckLoading, setIsCodeInputCheckLoading] = useState(false);
const { t } = useTranslation();
const { email: providerEmail, providerAuthToken, isProviderUserCompleted } = useProviderAuth();
if (providerAuthToken && isProviderUserCompleted) {
router.push(`/login?providerAuthToken=${encodeURIComponent(providerAuthToken)}`);
}
if (providerAuthToken && step < 3) {
setStep(3);
}
useEffect(() => {
const tryAuth = async () => {
@@ -57,6 +69,7 @@ export default function SignUp() {
if (step === 1 || step === 3 || step === 4) {
setStep(step + 1);
} else if (step === 2) {
setIsCodeInputCheckLoading(true);
// Checking if the code matches the email.
const response = await checkEmailVerificationCode({ email, code });
if (response.status === 200) {
@@ -66,6 +79,7 @@ export default function SignUp() {
} else {
setCodeError(true);
}
setIsCodeInputCheckLoading(false);
}
};
@@ -82,8 +96,65 @@ export default function SignUp() {
}
}, [step]);
const renderView = (registerStep: number) => {
if (isSignupWithEmail && registerStep === 1) {
return <EnterEmailStep email={email} setEmail={setEmail} incrementStep={incrementStep} />;
}
if (!isSignupWithEmail && registerStep === 1) {
return <InitialSignupStep setIsSignupWithEmail={setIsSignupWithEmail} />;
}
if (registerStep === 2) {
return (
<CodeInputStep
email={email}
incrementStep={incrementStep}
setCode={setCode}
codeError={codeError}
isCodeInputCheckLoading={isCodeInputCheckLoading}
/>
);
}
if (registerStep === 3) {
return (
<UserInfoStep
incrementStep={incrementStep}
email={email || providerEmail}
password={password}
setPassword={setPassword}
name={name}
setName={setName}
organizationName={organizationName}
setOrganizationName={setOrganizationName}
attributionSource={attributionSource}
setAttributionSource={setAttributionSource}
providerAuthToken={providerAuthToken}
/>
);
}
if (registerStep === 4) {
return (
<DownloadBackupPDF
incrementStep={incrementStep}
email={email || providerEmail}
password={password}
name={name}
/>
);
}
if (serverDetails?.emailConfigured) {
return <TeamInviteStep />;
}
return '';
};
return (
<div className="flex h-screen flex-col items-center justify-center bg-bunker-800">
<div className="flex min-h-screen flex-col justify-center bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700 px-6 pb-28 ">
<Head>
<title>{t('common.head-title', { title: t('signup.title') })}</title>
<link rel="icon" href="/infisical.ico" />
@@ -91,47 +162,10 @@ export default function SignUp() {
<meta property="og:title" content={t('signup.og-title') as string} />
<meta name="og:description" content={t('signup.og-description') as string} />
</Head>
<div className="flex flex-col items-center justify-center">
<Link href="/">
<div className="mb-2 flex cursor-pointer justify-center md:mb-8">
<Image src="/images/biglogo.png" height={90} width={120} alt="Infisical Wide Logo" />
</div>
</Link>
<form onSubmit={(e) => e.preventDefault()}>
{step === 1 ? (
<EnterEmailStep email={email} setEmail={setEmail} incrementStep={incrementStep} />
) : step === 2 ? (
<CodeInputStep
email={email}
incrementStep={incrementStep}
setCode={setCode}
codeError={codeError}
/>
) : step === 3 ? (
<UserInfoStep
incrementStep={incrementStep}
email={email}
password={password}
setPassword={setPassword}
firstName={firstName}
setFirstName={setFirstName}
lastName={lastName}
setLastName={setLastName}
/>
) : step === 4 ? (
<DownloadBackupPDF
incrementStep={incrementStep}
email={email}
password={password}
name={`${firstName} ${lastName}`}
/>
) : serverDetails?.emailConfigured ? (
<TeamInviteStep />
) : (
''
)}
</form>
<div className="mb-4 mt-20 flex justify-center">
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical Logo" />
</div>
<form onSubmit={(e) => e.preventDefault()}>{renderView(step)}</form>
</div>
);
}
+9 -9
View File
@@ -180,7 +180,7 @@ export default function SignupInvite() {
// Step 4 of the sign up process (download the emergency kit pdf)
const stepConfirmEmail = (
<div className="bg-bunker flex flex-col items-center w-full max-w-xs md:max-w-lg h-7/12 py-8 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
<div className="border border-mineshaft-600 bg-mineshaft-800 flex flex-col items-center w-full max-w-xs md:max-w-lg h-7/12 py-8 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
<p className="text-4xl text-center font-semibold mb-6 flex justify-center text-primary-100">
Confirm your email
</p>
@@ -219,8 +219,8 @@ export default function SignupInvite() {
// Because this is the invite signup - we directly go to the last step of signup (email is already verified)
const main = (
<div className="bg-bunker w-max mx-auto h-7/12 py-10 px-8 rounded-xl drop-shadow-xl mb-32 md:mb-16">
<p className="text-4xl font-bold flex justify-center mb-6 text-gray-400 mx-8 md:mx-16 text-transparent bg-clip-text bg-gradient-to-br from-sky-400 to-primary">
<div className="border border-mineshaft-600 bg-mineshaft-800 w-max mx-auto h-7/12 py-10 px-8 rounded-xl drop-shadow-xl mb-32 md:mb-16">
<p className="text-4xl font-bold flex justify-center mb-6 mx-8 md:mx-16 text-transparent bg-clip-text bg-gradient-to-tr from-mineshaft-300 to-white">
Almost there!
</p>
<div className="relative z-0 flex items-center justify-end w-full md:p-2 rounded-lg max-h-24">
@@ -322,8 +322,8 @@ export default function SignupInvite() {
// Step 4 of the sign up process (download the emergency kit pdf)
const step4 = (
<div className="bg-bunker flex flex-col items-center w-full max-w-xs md:max-w-lg h-7/12 py-8 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
<p className="text-4xl text-center font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-br from-sky-400 to-primary">
<div className="border border-mineshaft-600 bg-mineshaft-800 flex flex-col items-center w-full max-w-xs md:max-w-lg h-7/12 pt-8 pb-6 px-4 md:px-6 mx-1 mb-36 md:mb-16 rounded-xl drop-shadow-xl">
<p className="text-4xl text-center font-semibold flex justify-center text-transparent bg-clip-text bg-gradient-to-br from-white to-mineshaft-300">
Save your Emergency Kit
</p>
<div className="flex flex-col items-center justify-center w-full mt-4 md:mt-8 max-w-md text-gray-400 text-md rounded-md px-2">
@@ -336,7 +336,7 @@ export default function SignupInvite() {
<FontAwesomeIcon icon={faWarning} className="ml-2 mr-4 text-4xl" />
It contains your Secret Key which we cannot access or recover for you if you lose it.
</div>
<div className="flex flex-col items-center justify-center md:px-4 md:py-5 mt-2 px-2 py-3 max-h-24 max-w-max mx-auto text-lg">
<div className="flex flex-col items-center justify-center md:px-4 md:py-5 mt-4 px-2 py-3 max-h-24 max-w-max mx-auto text-lg">
<Button
text="Download PDF"
onButtonPressed={async () => {
@@ -368,14 +368,14 @@ export default function SignupInvite() {
);
return (
<div className="bg-bunker-800 h-screen flex flex-col items-center justify-center">
<div className="bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700 h-screen flex flex-col items-center justify-center">
<Head>
<title>Sign Up</title>
<link rel="icon" href="/infisical.ico" />
</Head>
<Link href="/">
<div className="flex justify-center mb-2 md:mb-4 opacity-80 cursor-pointer">
<Image src="/images/biglogo.png" height={90} width={120} alt="Infisical Wide Logo" />
<div className="mb-4 mt-20 flex justify-center">
<Image src="/images/gradientLogo.svg" height={90} width={120} alt="Infisical Logo" />
</div>
</Link>
{step === 1 ? stepConfirmEmail : step === 2 ? main : step4}