mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-06 09:26:47 +00:00
Fix: Formatting and support for selecting org (line 109-122)
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router";
|
||||||
import axios from "axios"
|
import axios from "axios";
|
||||||
import jwt_decode from "jwt-decode";
|
import jwt_decode from "jwt-decode";
|
||||||
|
|
||||||
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
|
||||||
@@ -10,179 +10,191 @@ import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
|||||||
import attemptLogin from "@app/components/utilities/attemptLogin";
|
import attemptLogin from "@app/components/utilities/attemptLogin";
|
||||||
import { Button, Input } from "@app/components/v2";
|
import { Button, Input } from "@app/components/v2";
|
||||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||||
|
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||||
|
|
||||||
import { navigateUserToOrg } from "../../Login.utils";
|
import { navigateUserToOrg, navigateUserToSelectOrg } from "../../Login.utils";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
providerAuthToken: string;
|
providerAuthToken: string;
|
||||||
email: string;
|
email: string;
|
||||||
password: string;
|
password: string;
|
||||||
setPassword: (password: string) => void;
|
setPassword: (password: string) => void;
|
||||||
setStep: (step: number) => void;
|
setStep: (step: number) => void;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const PasswordStep = ({
|
export const PasswordStep = ({
|
||||||
providerAuthToken,
|
providerAuthToken,
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
setPassword,
|
setPassword,
|
||||||
setStep,
|
setStep
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const { createNotification } = useNotificationContext();
|
const { createNotification } = useNotificationContext();
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { mutateAsync } = useUpdateUserAuthMethods();
|
const { mutateAsync } = useUpdateUserAuthMethods();
|
||||||
|
|
||||||
const {
|
const { callbackPort, isLinkingRequired, authMethod, organizationId } = jwt_decode(
|
||||||
callbackPort,
|
providerAuthToken
|
||||||
isLinkingRequired,
|
) as any;
|
||||||
authMethod,
|
|
||||||
organizationId
|
|
||||||
} = jwt_decode(providerAuthToken) as any;
|
|
||||||
|
|
||||||
const handleLogin = async (e:React.FormEvent) => {
|
const handleLogin = async (e: React.FormEvent) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
if (callbackPort) {
|
if (callbackPort) {
|
||||||
// attemptCliLogin
|
// attemptCliLogin
|
||||||
const isCliLoginSuccessful = await attemptCliLogin({
|
const isCliLoginSuccessful = await attemptCliLogin({
|
||||||
email,
|
email,
|
||||||
password,
|
password,
|
||||||
providerAuthToken
|
providerAuthToken
|
||||||
})
|
});
|
||||||
|
|
||||||
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
||||||
|
if (isCliLoginSuccessful.mfaEnabled) {
|
||||||
if (isCliLoginSuccessful.mfaEnabled) {
|
// case: login requires MFA step
|
||||||
// case: login requires MFA step
|
setStep(2);
|
||||||
setStep(2);
|
|
||||||
setIsLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// case: login was successful
|
|
||||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`
|
|
||||||
|
|
||||||
// send request to server endpoint
|
|
||||||
const instance = axios.create()
|
|
||||||
await instance.post(cliUrl, { ...isCliLoginSuccessful.loginResponse })
|
|
||||||
|
|
||||||
// cli page
|
|
||||||
router.push("/cli-redirect");
|
|
||||||
|
|
||||||
// on success, router.push to cli Login Successful page
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const loginAttempt = await attemptLogin({
|
|
||||||
email,
|
|
||||||
password,
|
|
||||||
providerAuthToken,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (loginAttempt && loginAttempt.success) {
|
|
||||||
// case: login was successful
|
|
||||||
|
|
||||||
if (loginAttempt.mfaEnabled) {
|
|
||||||
// TODO: deal with MFA
|
|
||||||
// case: login requires MFA step
|
|
||||||
setIsLoading(false);
|
|
||||||
setStep(2);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// case: login does not require MFA step
|
|
||||||
setIsLoading(false);
|
|
||||||
createNotification({
|
|
||||||
text: "Successfully logged in",
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isLinkingRequired) {
|
|
||||||
const user = await fetchUserDetails();
|
|
||||||
const newAuthMethods = [...user.authMethods, authMethod]
|
|
||||||
await mutateAsync({
|
|
||||||
authMethods: newAuthMethods
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await navigateUserToOrg(router, organizationId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
createNotification({
|
return;
|
||||||
text: "Login unsuccessful. Double-check your master password and try again.",
|
}
|
||||||
type: "error"
|
// case: login was successful
|
||||||
});
|
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||||
console.error(err);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
// send request to server endpoint
|
||||||
<form
|
const instance = axios.create();
|
||||||
onSubmit={handleLogin}
|
await instance.post(cliUrl, { ...isCliLoginSuccessful.loginResponse });
|
||||||
className="h-full mx-auto w-full max-w-md px-6 pt-8"
|
|
||||||
|
// cli page
|
||||||
|
router.push("/cli-redirect");
|
||||||
|
|
||||||
|
// on success, router.push to cli Login Successful page
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const loginAttempt = await attemptLogin({
|
||||||
|
email,
|
||||||
|
password,
|
||||||
|
providerAuthToken
|
||||||
|
});
|
||||||
|
|
||||||
|
if (loginAttempt && loginAttempt.success) {
|
||||||
|
// case: login was successful
|
||||||
|
|
||||||
|
if (loginAttempt.mfaEnabled) {
|
||||||
|
// TODO: deal with MFA
|
||||||
|
// case: login requires MFA step
|
||||||
|
setIsLoading(false);
|
||||||
|
setStep(2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// case: login does not require MFA step
|
||||||
|
setIsLoading(false);
|
||||||
|
createNotification({
|
||||||
|
text: "Successfully logged in",
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (isLinkingRequired) {
|
||||||
|
const user = await fetchUserDetails();
|
||||||
|
const newAuthMethods = [...user.authMethods, authMethod];
|
||||||
|
await mutateAsync({
|
||||||
|
authMethods: newAuthMethods
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// case: organization ID is present from the provider auth token -- navigate directly to the org
|
||||||
|
if (organizationId) {
|
||||||
|
await navigateUserToOrg(router, organizationId);
|
||||||
|
}
|
||||||
|
// case: no organization ID is present -- navigate to the select org page IF the user has any orgs
|
||||||
|
// if the user has no orgs, navigate to the create org page
|
||||||
|
else {
|
||||||
|
const userOrgs = await fetchOrganizations();
|
||||||
|
|
||||||
|
if (userOrgs.length > 0) {
|
||||||
|
navigateUserToSelectOrg(router);
|
||||||
|
} else {
|
||||||
|
await navigateUserToOrg(router);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setIsLoading(false);
|
||||||
|
createNotification({
|
||||||
|
text: "Login unsuccessful. Double-check your master password and try again.",
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
console.error(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleLogin} className="mx-auto h-full w-full max-w-md px-6 pt-8">
|
||||||
|
<div className="mb-8">
|
||||||
|
<p className="mx-auto mb-4 flex w-max justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-center text-xl font-medium text-transparent">
|
||||||
|
{isLinkingRequired ? "Link your account" : "What's your Infisical password?"}
|
||||||
|
</p>
|
||||||
|
{isLinkingRequired && (
|
||||||
|
<div className="mx-auto flex w-max flex-col items-center text-xs text-bunker-400">
|
||||||
|
<span className="max-w-sm px-4 text-center duration-200">
|
||||||
|
An existing account without this SSO authentication method enabled was found under the
|
||||||
|
same email. Login with your password to link the account.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className="relative mx-auto flex max-h-24 w-1/4 w-full min-w-[22rem] items-center justify-center rounded-lg md:max-h-28 lg:w-1/6">
|
||||||
|
<div className="flex max-h-24 w-full items-center justify-center rounded-lg md:max-h-28">
|
||||||
|
<Input
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
type="password"
|
||||||
|
placeholder="Enter your password..."
|
||||||
|
isRequired
|
||||||
|
autoComplete="current-password"
|
||||||
|
id="current-password"
|
||||||
|
className="h-12"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mx-auto mt-4 flex w-1/4 w-full min-w-[22rem] items-center justify-center rounded-md text-center lg:w-1/6">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
colorSchema="primary"
|
||||||
|
variant="outline_bg"
|
||||||
|
isFullWidth
|
||||||
|
isLoading={isLoading}
|
||||||
|
className="h-14"
|
||||||
>
|
>
|
||||||
<div className="mb-8">
|
{t("login.login")}
|
||||||
<p className="mx-auto 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-4">
|
</Button>
|
||||||
{isLinkingRequired ? "Link your account" : "What's your Infisical password?"}
|
</div>
|
||||||
</p>
|
<div className="mx-auto mt-4 flex w-max flex-col items-center text-xs text-bunker-400">
|
||||||
{isLinkingRequired && (
|
<span className="max-w-sm px-4 text-center duration-200">
|
||||||
<div className="text-bunker-400 text-xs flex flex-col items-center w-max mx-auto">
|
Infisical Master Password serves as a decryption mechanism so that even Google is not able
|
||||||
<span className='duration-200 max-w-sm text-center px-4'>
|
to access your secrets.
|
||||||
An existing account without this SSO authentication method enabled was found under the same email. Login with your password to link the account.
|
</span>
|
||||||
</span>
|
<Link href="/verify-email">
|
||||||
</div>
|
<span className="mt-2 cursor-pointer duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4">
|
||||||
)}
|
{t("login.forgot-password")}
|
||||||
</div>
|
</span>
|
||||||
<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">
|
</Link>
|
||||||
<div className="flex items-center justify-center w-full rounded-lg max-h-24 md:max-h-28">
|
</div>
|
||||||
<Input
|
<div className="flex flex-row items-center justify-center">
|
||||||
value={password}
|
<button
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
onClick={() => {
|
||||||
type="password"
|
router.push("/login");
|
||||||
placeholder="Enter your password..."
|
}}
|
||||||
isRequired
|
type="button"
|
||||||
autoComplete="current-password"
|
className="mt-2 cursor-pointer text-xs text-bunker-400 duration-200 hover:text-bunker-200 hover:underline hover:decoration-primary-700 hover:underline-offset-4"
|
||||||
id="current-password"
|
>
|
||||||
className="h-12"
|
{t("login.other-option")}
|
||||||
/>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</form>
|
||||||
<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
|
};
|
||||||
type="submit"
|
|
||||||
colorSchema="primary"
|
|
||||||
variant="outline_bg"
|
|
||||||
isFullWidth
|
|
||||||
isLoading={isLoading}
|
|
||||||
className="h-14"
|
|
||||||
>
|
|
||||||
{t("login.login")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
<div className="text-bunker-400 text-xs flex flex-col items-center w-max mx-auto mt-4">
|
|
||||||
<span className='duration-200 max-w-sm text-center px-4'>
|
|
||||||
Infisical Master Password serves as a decryption mechanism so that even Google is not able to access your secrets.
|
|
||||||
</span>
|
|
||||||
<Link href="/verify-email">
|
|
||||||
<span className='hover:underline mt-2 hover:underline-offset-4 hover:decoration-primary-700 hover:text-bunker-200 duration-200 cursor-pointer'>{t("login.forgot-password")}</span>
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-row items-center justify-center">
|
|
||||||
<button
|
|
||||||
onClick={() => {
|
|
||||||
router.push("/login");
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
className="text-bunker-400 text-xs 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>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user