mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-07 11:27:32 +00:00
developed initial version of new login page
This commit is contained in:
@@ -84,7 +84,7 @@ router.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
router.get(
|
router.get(
|
||||||
'/login/federated/google',
|
'/login/google',
|
||||||
authLimiter,
|
authLimiter,
|
||||||
passport.authenticate('google', {
|
passport.authenticate('google', {
|
||||||
scope: ['profile', 'email'],
|
scope: ['profile', 'email'],
|
||||||
|
|||||||
@@ -47,7 +47,10 @@ export default function LoginStep ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const isLoginSuccessful = await attemptLogin(email, password);
|
const isLoginSuccessful = await attemptLogin({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
});
|
||||||
if (isLoginSuccessful && isLoginSuccessful.success) {
|
if (isLoginSuccessful && isLoginSuccessful.success) {
|
||||||
// case: login was successful
|
// case: login was successful
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { useRouter } from 'next/router';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
|
||||||
|
import Button from '@app/components/basic/buttons/Button';
|
||||||
|
import Error from '@app/components/basic/Error';
|
||||||
|
import InputField from '@app/components/basic/InputField';
|
||||||
|
import attemptLogin from '@app/components/utilities/attemptLogin';
|
||||||
|
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||||
|
|
||||||
|
import SecurityClient from '../utilities/SecurityClient';
|
||||||
|
|
||||||
|
export default function PasswordInputStep({
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
setPassword,
|
||||||
|
setProviderAuthToken,
|
||||||
|
setStep
|
||||||
|
}: {
|
||||||
|
email: string;
|
||||||
|
userId: string;
|
||||||
|
password: string;
|
||||||
|
setPassword: (password: string) => void;
|
||||||
|
setProviderAuthToken: (value: string) => void;
|
||||||
|
setStep: (step: number) => void;
|
||||||
|
}) {
|
||||||
|
const router = useRouter();
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const [loginError, setLoginError] = useState(false);
|
||||||
|
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const handleLogin = async () => {
|
||||||
|
try {
|
||||||
|
if (!userId || !password) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(true);
|
||||||
|
const isLoginSuccessful = await attemptLogin({
|
||||||
|
userId,
|
||||||
|
email,
|
||||||
|
password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLoginSuccessful && isLoginSuccessful.success) {
|
||||||
|
// case: login was successful
|
||||||
|
|
||||||
|
if (isLoginSuccessful.mfaEnabled) {
|
||||||
|
// case: login requires MFA step
|
||||||
|
setStep(2);
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// case: login does not require MFA step
|
||||||
|
router.push(`/dashboard/${localStorage.getItem('projectData.id')}`);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setLoginError(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={(e) => e.preventDefault()}>
|
||||||
|
<div className="h-7/12 mx-auto w-full max-w-md rounded-xl bg-bunker py-4 px-6 pt-8 drop-shadow-xl">
|
||||||
|
<p className="mx-auto mb-6 flex w-max justify-center text-3xl font-semibold text-bunker-100">
|
||||||
|
{t('login:login')}
|
||||||
|
</p>
|
||||||
|
<div className="relative mt-6 flex max-h-24 w-full items-center justify-center rounded-lg md:mt-2 md:max-h-28 md:p-2">
|
||||||
|
<InputField
|
||||||
|
label={t('common:password')}
|
||||||
|
onChangeHandler={setPassword}
|
||||||
|
type="password"
|
||||||
|
value={password}
|
||||||
|
placeholder=""
|
||||||
|
isRequired
|
||||||
|
autoComplete="current-password"
|
||||||
|
id="current-password"
|
||||||
|
/>
|
||||||
|
<div className="absolute top-2 right-3 cursor-pointer text-sm text-primary-700 duration-200 hover:text-primary">
|
||||||
|
<Link href="/verify-email">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ml-1.5 text-sm font-normal text-primary-700 underline-offset-4 duration-200 hover:text-primary"
|
||||||
|
>
|
||||||
|
{t('login:forgot-password')}
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!isLoading && loginError && <Error text={t('login:error-login') ?? ''} />}
|
||||||
|
<div className="mx-auto mt-4 flex max-h-20 w-full max-w-md flex-col items-center justify-center text-sm md:p-2">
|
||||||
|
<div className="text-l m-8 mt-6 px-8 py-3 text-lg">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
text={t('login:login') ?? ''}
|
||||||
|
onButtonPressed={async () => handleLogin()}
|
||||||
|
loading={isLoading}
|
||||||
|
size="lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 flex flex-row items-center justify-center md:pb-4">
|
||||||
|
<p className="flex w-max justify-center text-sm text-gray-400">{t('login:need-account')}</p>
|
||||||
|
<Link href="/signup">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="ml-1.5 text-sm font-normal text-primary-700 underline-offset-4 duration-200 hover:text-primary"
|
||||||
|
>
|
||||||
|
{t('login:create-account')}
|
||||||
|
</button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-row items-center justify-center">
|
||||||
|
<button
|
||||||
|
onClick={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
SecurityClient.setProviderAuthToken('');
|
||||||
|
setProviderAuthToken('');
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
className="ml-1.5 text-sm font-normal text-primary-700 underline-offset-4 duration-200 hover:text-primary"
|
||||||
|
>
|
||||||
|
Log in with other options.
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getStaticProps = getTranslatedStaticProps(['auth', 'login']);
|
||||||
@@ -26,20 +26,33 @@ interface IsLoginSuccessful {
|
|||||||
* @param {string} password - password of user to log in
|
* @param {string} password - password of user to log in
|
||||||
*/
|
*/
|
||||||
const attemptLogin = async (
|
const attemptLogin = async (
|
||||||
email: string,
|
{
|
||||||
password: string
|
email,
|
||||||
|
password,
|
||||||
|
userId,
|
||||||
|
}: {
|
||||||
|
email: string;
|
||||||
|
userId?: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
): Promise<IsLoginSuccessful> => {
|
): Promise<IsLoginSuccessful> => {
|
||||||
|
|
||||||
|
const username = userId ?? email;
|
||||||
const telemetry = new Telemetry().getInstance();
|
const telemetry = new Telemetry().getInstance();
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
client.init(
|
client.init(
|
||||||
{
|
{
|
||||||
username: email,
|
username,
|
||||||
password
|
password
|
||||||
},
|
},
|
||||||
async () => {
|
async () => {
|
||||||
try {
|
try {
|
||||||
const clientPublicKey = client.getPublicKey();
|
const clientPublicKey = client.getPublicKey();
|
||||||
const { serverPublicKey, salt } = await login1(email, clientPublicKey);
|
const { serverPublicKey, salt } = await login1({
|
||||||
|
email,
|
||||||
|
clientPublicKey,
|
||||||
|
userId,
|
||||||
|
});
|
||||||
|
|
||||||
client.setSalt(salt);
|
client.setSalt(salt);
|
||||||
client.setServerPublicKey(serverPublicKey);
|
client.setServerPublicKey(serverPublicKey);
|
||||||
@@ -57,8 +70,11 @@ const attemptLogin = async (
|
|||||||
iv,
|
iv,
|
||||||
tag
|
tag
|
||||||
} = await login2(
|
} = await login2(
|
||||||
email,
|
{
|
||||||
clientProof
|
email,
|
||||||
|
userId,
|
||||||
|
clientProof,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (mfaEnabled) {
|
if (mfaEnabled) {
|
||||||
@@ -137,4 +153,4 @@ const attemptLogin = async (
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export default attemptLogin;
|
export default attemptLogin;
|
||||||
|
|||||||
@@ -9,16 +9,17 @@ interface Login1 {
|
|||||||
* @param {*} clientPublicKey
|
* @param {*} clientPublicKey
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const login1 = async (email: string, clientPublicKey: string) => {
|
const login1 = async (loginDetails: {
|
||||||
|
email: string;
|
||||||
|
clientPublicKey: string;
|
||||||
|
userId?: string;
|
||||||
|
}) => {
|
||||||
const response = await fetch("/api/v2/auth/login1", {
|
const response = await fetch("/api/v2/auth/login1", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(loginDetails),
|
||||||
email,
|
|
||||||
clientPublicKey,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
// need precise error handling about the status code
|
// need precise error handling about the status code
|
||||||
if (response?.status === 200) {
|
if (response?.status === 200) {
|
||||||
|
|||||||
@@ -17,16 +17,17 @@ interface Login2Response {
|
|||||||
* @param {*} clientPublicKey
|
* @param {*} clientPublicKey
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
const login2 = async (email: string, clientProof: string) => {
|
const login2 = async (loginDetails: {
|
||||||
|
email: string;
|
||||||
|
clientProof: string;
|
||||||
|
userId?: string;
|
||||||
|
}) => {
|
||||||
const response = await fetch('/api/v2/auth/login2', {
|
const response = await fetch('/api/v2/auth/login2', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify(loginDetails),
|
||||||
email,
|
|
||||||
clientProof
|
|
||||||
}),
|
|
||||||
credentials: 'include'
|
credentials: 'include'
|
||||||
});
|
});
|
||||||
// need precise error handling about the status code
|
// need precise error handling about the status code
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import { useTranslation } from 'next-i18next';
|
|||||||
import ListBox from '@app/components/basic/Listbox';
|
import ListBox from '@app/components/basic/Listbox';
|
||||||
import LoginStep from '@app/components/login/LoginStep';
|
import LoginStep from '@app/components/login/LoginStep';
|
||||||
import MFAStep from '@app/components/login/MFAStep';
|
import MFAStep from '@app/components/login/MFAStep';
|
||||||
|
import PasswordInputStep from '@app/components/login/PasswordInputStep';
|
||||||
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
import { getTranslatedStaticProps } from '@app/components/utilities/withTranslateProps';
|
||||||
|
import { useProviderAuth } from '@app/hooks/useProviderAuth';
|
||||||
import { isLoggedIn } from '@app/reactQuery';
|
import { isLoggedIn } from '@app/reactQuery';
|
||||||
|
|
||||||
import getWorkspaces from './api/workspace/getWorkspaces';
|
import getWorkspaces from './api/workspace/getWorkspaces';
|
||||||
@@ -20,8 +22,14 @@ export default function Login() {
|
|||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const lang = router.locale ?? 'en';
|
const lang = router.locale ?? 'en';
|
||||||
|
const [isLoginWithEmail, setIsLoginWithEmail] = useState(false);
|
||||||
|
const {
|
||||||
|
providerAuthToken,
|
||||||
|
userId,
|
||||||
|
email: providerEmail,
|
||||||
|
setProviderAuthToken
|
||||||
|
} = useProviderAuth();
|
||||||
|
|
||||||
|
|
||||||
const setLanguage = async (to: string) => {
|
const setLanguage = async (to: string) => {
|
||||||
router.push('/login', '/login', { locale: to });
|
router.push('/login', '/login', { locale: to });
|
||||||
localStorage.setItem('lang', to);
|
localStorage.setItem('lang', to);
|
||||||
@@ -44,30 +52,58 @@ export default function Login() {
|
|||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const renderStep = (loginStep: number) => {
|
const renderView = (loginStep: number) => {
|
||||||
// TODO: add MFA step
|
|
||||||
switch (loginStep) {
|
if (providerAuthToken && step === 1) {
|
||||||
case 1:
|
return (
|
||||||
return (
|
<PasswordInputStep
|
||||||
<LoginStep
|
userId={userId}
|
||||||
email={email}
|
email={providerEmail}
|
||||||
setEmail={setEmail}
|
password={password}
|
||||||
password={password}
|
setPassword={setPassword}
|
||||||
setPassword={setPassword}
|
setProviderAuthToken={setProviderAuthToken}
|
||||||
setStep={setStep}
|
setStep={setStep}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
case 2:
|
|
||||||
// TODO: add MFA step
|
|
||||||
return (
|
|
||||||
<MFAStep
|
|
||||||
email={email}
|
|
||||||
password={password}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
return <div />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isLoginWithEmail && loginStep === 1) {
|
||||||
|
return (
|
||||||
|
<LoginStep
|
||||||
|
email={email}
|
||||||
|
setEmail={setEmail}
|
||||||
|
password={password}
|
||||||
|
setPassword={setPassword}
|
||||||
|
setStep={setStep}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoginWithEmail && loginStep === 1) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button type='button' className='text-white' onClick={() => {
|
||||||
|
window.open('/api/v1/auth/login/google')
|
||||||
|
}}>
|
||||||
|
Continue with Google
|
||||||
|
</button>
|
||||||
|
<button type='button' className='text-white' onClick={() => {
|
||||||
|
setIsLoginWithEmail(true);
|
||||||
|
}}>
|
||||||
|
Continue with Email
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (step === 2) {
|
||||||
|
<MFAStep
|
||||||
|
email={email || providerEmail}
|
||||||
|
password={password}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div />
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -84,7 +120,7 @@ export default function Login() {
|
|||||||
<Image src="/images/biglogo.png" height={90} width={120} alt="long logo" />
|
<Image src="/images/biglogo.png" height={90} width={120} alt="long logo" />
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
{renderStep(step)}
|
{renderView(step)}
|
||||||
<div className="absolute right-4 top-0 mt-4 flex items-center justify-center">
|
<div className="absolute right-4 top-0 mt-4 flex items-center justify-center">
|
||||||
<div className="w-48 mx-auto">
|
<div className="w-48 mx-auto">
|
||||||
<ListBox
|
<ListBox
|
||||||
|
|||||||
Reference in New Issue
Block a user