mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Merge pull request #1944 from akhilmhdh/feat/srp-handover
Removing Master password for Oauth/SSO/LDAP users.
This commit is contained in:
931
frontend/package-lock.json
generated
931
frontend/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -161,6 +161,7 @@ export default function UserInfoStep({
|
||||
|
||||
const response = await completeAccountSignup({
|
||||
email,
|
||||
password,
|
||||
firstName: name.split(" ")[0],
|
||||
lastName: name.split(" ").slice(1).join(" "),
|
||||
protectedKey,
|
||||
|
||||
@@ -72,6 +72,7 @@ const attemptChangePassword = ({ email, currentPassword, newPassword }: Params):
|
||||
});
|
||||
|
||||
await changePassword({
|
||||
password: newPassword,
|
||||
clientProof,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
|
||||
@@ -71,6 +71,7 @@ const attemptLogin = async ({
|
||||
tag
|
||||
} = await login2({
|
||||
email,
|
||||
password,
|
||||
clientProof,
|
||||
providerAuthToken,
|
||||
captchaToken
|
||||
|
||||
@@ -62,6 +62,7 @@ const attemptLogin = async ({
|
||||
} = await login2({
|
||||
captchaToken,
|
||||
email,
|
||||
password,
|
||||
clientProof,
|
||||
providerAuthToken
|
||||
});
|
||||
|
||||
@@ -10,6 +10,7 @@ export type TServerConfig = {
|
||||
|
||||
export type TCreateAdminUserDTO = {
|
||||
email: string;
|
||||
password: string;
|
||||
firstName: string;
|
||||
lastName?: string;
|
||||
protectedKey: string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export {
|
||||
useGetAuthToken,
|
||||
useOauthTokenExchange,
|
||||
useResetPassword,
|
||||
useSelectOrganization,
|
||||
useSendMfaToken,
|
||||
@@ -7,4 +8,5 @@ export {
|
||||
useSendVerificationEmail,
|
||||
useVerifyMfaToken,
|
||||
useVerifyPasswordResetCode,
|
||||
useVerifySignupEmailVerificationCode} from "./queries";
|
||||
useVerifySignupEmailVerificationCode
|
||||
} from "./queries";
|
||||
|
||||
@@ -23,6 +23,7 @@ import {
|
||||
SendMfaTokenDTO,
|
||||
SRP1DTO,
|
||||
SRPR1Res,
|
||||
TOauthTokenExchangeDTO,
|
||||
VerifyMfaTokenDTO,
|
||||
VerifyMfaTokenRes,
|
||||
VerifySignupInviteDTO
|
||||
@@ -92,6 +93,7 @@ export const useLogin2 = () => {
|
||||
mutationFn: async (details: {
|
||||
email: string;
|
||||
clientProof: string;
|
||||
password: string;
|
||||
providerAuthToken?: string;
|
||||
}) => {
|
||||
return login2(details);
|
||||
@@ -99,6 +101,20 @@ export const useLogin2 = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const oauthTokenExchange = async (details: TOauthTokenExchangeDTO) => {
|
||||
const { data } = await apiRequest.post<Login2Res>("/api/v1/sso/token-exchange", details);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const useOauthTokenExchange = () => {
|
||||
// note: use after srp1
|
||||
return useMutation({
|
||||
mutationFn: async (details: TOauthTokenExchangeDTO) => {
|
||||
return oauthTokenExchange(details);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const srp1 = async (details: SRP1DTO) => {
|
||||
const { data } = await apiRequest.post<SRPR1Res>("/api/v1/password/srp1", details);
|
||||
return data;
|
||||
|
||||
@@ -23,6 +23,11 @@ export type VerifyMfaTokenRes = {
|
||||
tag: string;
|
||||
};
|
||||
|
||||
export type TOauthTokenExchangeDTO = {
|
||||
providerAuthToken: string;
|
||||
email: string;
|
||||
};
|
||||
|
||||
export type Login1DTO = {
|
||||
email: string;
|
||||
clientPublicKey: string;
|
||||
@@ -34,6 +39,7 @@ export type Login2DTO = {
|
||||
email: string;
|
||||
clientProof: string;
|
||||
providerAuthToken?: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type Login1Res = {
|
||||
@@ -86,6 +92,7 @@ export type CompleteAccountDTO = {
|
||||
encryptedPrivateKeyTag: string;
|
||||
salt: string;
|
||||
verifier: string;
|
||||
password: string;
|
||||
};
|
||||
|
||||
export type CompleteAccountSignupDTO = CompleteAccountDTO & {
|
||||
@@ -101,6 +108,7 @@ export type VerifySignupInviteDTO = {
|
||||
};
|
||||
|
||||
export type ChangePasswordDTO = {
|
||||
password: string;
|
||||
clientProof: string;
|
||||
protectedKey: string;
|
||||
protectedKeyIV: string;
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
|
||||
export const userKeys = {
|
||||
getUser: ["user"] as const,
|
||||
getPrivateKey: ["user"] as const,
|
||||
userAction: ["user-action"] as const,
|
||||
getOrgUsers: (orgId: string) => [{ orgId }, "user"],
|
||||
myIp: ["ip"] as const,
|
||||
@@ -351,3 +352,11 @@ export const useGetMyOrganizationProjects = (orgId: string) => {
|
||||
enabled: true
|
||||
});
|
||||
};
|
||||
|
||||
export const fetchMyPrivateKey = async () => {
|
||||
const {
|
||||
data: { privateKey }
|
||||
} = await apiRequest.get<{ privateKey: string }>("/api/v1/user/private-key");
|
||||
|
||||
return privateKey;
|
||||
};
|
||||
|
||||
@@ -149,6 +149,7 @@ export default function SignupInvite() {
|
||||
|
||||
const { token: jwtToken } = await completeAccountSignupInvite({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
protectedKey,
|
||||
|
||||
@@ -9,13 +9,12 @@ import Error from "@app/components/basic/Error";
|
||||
import { createNotification } from "@app/components/notifications";
|
||||
import attemptCliLoginMfa from "@app/components/utilities/attemptCliLoginMfa";
|
||||
import attemptLoginMfa from "@app/components/utilities/attemptLoginMfa";
|
||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||
import { Button } from "@app/components/v2";
|
||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||
import { useSendMfaToken } from "@app/hooks/api/auth";
|
||||
import { useSelectOrganization } from "@app/hooks/api/auth/queries";
|
||||
import { useSelectOrganization, verifyMfaToken } from "@app/hooks/api/auth/queries";
|
||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||
import { AuthMethod } from "@app/hooks/api/users/types";
|
||||
import { fetchMyPrivateKey } from "@app/hooks/api/users/queries";
|
||||
|
||||
import { navigateUserToOrg, navigateUserToSelectOrg } from "../../Login.utils";
|
||||
|
||||
@@ -56,15 +55,59 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const sendMfaToken = useSendMfaToken();
|
||||
const { mutateAsync: updateUserAuthMethodsMutateAsync } = useUpdateUserAuthMethods();
|
||||
const { mutateAsync: selectOrganization } = useSelectOrganization();
|
||||
|
||||
// They don't have password
|
||||
const handleLoginMfaOauth = async (callbackPort: string, organizationId?: string) => {
|
||||
setIsLoading(true);
|
||||
const { token } = await verifyMfaToken({
|
||||
email,
|
||||
mfaCode
|
||||
});
|
||||
//
|
||||
// unset temporary (MFA) JWT token and set JWT token
|
||||
SecurityClient.setMfaToken("");
|
||||
SecurityClient.setToken(token);
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
const privateKey = await fetchMyPrivateKey();
|
||||
localStorage.setItem("PRIVATE_KEY", privateKey);
|
||||
|
||||
// case: organization ID is present from the provider auth token -- select the org and use the new jwt token in the CLI, then navigate to the org
|
||||
if (organizationId) {
|
||||
const { token: newJwtToken } = await selectOrganization({ organizationId });
|
||||
if (callbackPort) {
|
||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||
const instance = axios.create();
|
||||
await instance.post(cliUrl, {
|
||||
email,
|
||||
privateKey,
|
||||
JTWToken: newJwtToken
|
||||
});
|
||||
}
|
||||
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();
|
||||
|
||||
// case: user has orgs, so we navigate the user to select an org
|
||||
if (userOrgs.length > 0) {
|
||||
navigateUserToSelectOrg(router, callbackPort);
|
||||
}
|
||||
// case: no orgs found, so we navigate the user to create an org
|
||||
// cli login will fail in this case
|
||||
else {
|
||||
await navigateUserToOrg(router);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleLoginMfa = async () => {
|
||||
try {
|
||||
let isLinkingRequired: undefined | boolean;
|
||||
let callbackPort: undefined | string;
|
||||
let authMethod: undefined | AuthMethod;
|
||||
let organizationId: undefined | string;
|
||||
let hasExchangedPrivateKey: undefined | boolean;
|
||||
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
|
||||
@@ -73,10 +116,9 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
||||
if (providerAuthToken) {
|
||||
const decodedToken = jwt_decode(providerAuthToken) as any;
|
||||
|
||||
isLinkingRequired = decodedToken.isLinkingRequired;
|
||||
callbackPort = decodedToken.callbackPort;
|
||||
authMethod = decodedToken.authMethod;
|
||||
organizationId = decodedToken?.organizationId;
|
||||
hasExchangedPrivateKey = decodedToken?.hasExchangedPrivateKey;
|
||||
}
|
||||
|
||||
if (mfaCode.length !== 6) {
|
||||
@@ -87,6 +129,11 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (hasExchangedPrivateKey) {
|
||||
await handleLoginMfaOauth(callbackPort as string, organizationId);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
if (callbackPort) {
|
||||
// attemptCliLogin
|
||||
@@ -145,14 +192,6 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => {
|
||||
type: "success"
|
||||
});
|
||||
|
||||
if (isLinkingRequired && authMethod) {
|
||||
const user = await fetchUserDetails();
|
||||
const newAuthMethods = [...user.authMethods, authMethod];
|
||||
await updateUserAuthMethodsMutateAsync({
|
||||
authMethods: newAuthMethods
|
||||
});
|
||||
}
|
||||
|
||||
if (organizationId) {
|
||||
await navigateUserToOrg(router, organizationId);
|
||||
} else {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { useEffect, useRef,useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
@@ -10,11 +10,11 @@ import { createNotification } from "@app/components/notifications";
|
||||
import attemptCliLogin from "@app/components/utilities/attemptCliLogin";
|
||||
import attemptLogin from "@app/components/utilities/attemptLogin";
|
||||
import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config";
|
||||
import { Button, Input } from "@app/components/v2";
|
||||
import { useUpdateUserAuthMethods } from "@app/hooks/api";
|
||||
import { useSelectOrganization } from "@app/hooks/api/auth/queries";
|
||||
import SecurityClient from "@app/components/utilities/SecurityClient";
|
||||
import { Button, Input, Spinner } from "@app/components/v2";
|
||||
import { useOauthTokenExchange, useSelectOrganization } from "@app/hooks/api";
|
||||
import { fetchOrganizations } from "@app/hooks/api/organization/queries";
|
||||
import { fetchUserDetails } from "@app/hooks/api/users/queries";
|
||||
import { fetchMyPrivateKey } from "@app/hooks/api/users/queries";
|
||||
|
||||
import { navigateUserToOrg, navigateUserToSelectOrg } from "../../Login.utils";
|
||||
|
||||
@@ -36,12 +36,94 @@ export const PasswordStep = ({
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
const { mutateAsync } = useUpdateUserAuthMethods();
|
||||
const { mutateAsync: selectOrganization } = useSelectOrganization();
|
||||
const { mutateAsync: oauthTokenExchange } = useOauthTokenExchange();
|
||||
|
||||
const { callbackPort, isLinkingRequired, authMethod, organizationId } = jwt_decode(
|
||||
providerAuthToken
|
||||
) as any;
|
||||
const { callbackPort, organizationId, hasExchangedPrivateKey } =
|
||||
jwt_decode(providerAuthToken) as any;
|
||||
|
||||
const handleExchange = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const oauthLogin = await oauthTokenExchange({
|
||||
email,
|
||||
providerAuthToken
|
||||
});
|
||||
|
||||
// attemptCliLogin
|
||||
if (oauthLogin.mfaEnabled) {
|
||||
SecurityClient.setMfaToken(oauthLogin.token);
|
||||
// case: login requires MFA step
|
||||
setStep(2);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
const cliUrl = `http://127.0.0.1:${callbackPort}/`;
|
||||
|
||||
// case: MFA is not enabled
|
||||
|
||||
// unset provider auth token in case it was used
|
||||
SecurityClient.setProviderAuthToken("");
|
||||
// set JWT token
|
||||
SecurityClient.setToken(oauthLogin.token);
|
||||
|
||||
const privateKey = await fetchMyPrivateKey();
|
||||
localStorage.setItem("PRIVATE_KEY", privateKey);
|
||||
|
||||
// case: organization ID is present from the provider auth token -- select the org and use the new jwt token in the CLI, then navigate to the org
|
||||
if (organizationId) {
|
||||
const { token: newJwtToken } = await selectOrganization({ organizationId });
|
||||
if (callbackPort) {
|
||||
console.log("organization id was present. new JWT token to be used in CLI:", newJwtToken);
|
||||
const instance = axios.create();
|
||||
await instance.post(cliUrl, {
|
||||
privateKey,
|
||||
email,
|
||||
JTWToken: newJwtToken
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// case: user has orgs, so we navigate the user to select an org
|
||||
if (userOrgs.length > 0) {
|
||||
navigateUserToSelectOrg(router, callbackPort);
|
||||
}
|
||||
// case: no orgs found, so we navigate the user to create an org
|
||||
else {
|
||||
await navigateUserToOrg(router);
|
||||
}
|
||||
}
|
||||
} catch (err: any) {
|
||||
setIsLoading(false);
|
||||
console.error(err);
|
||||
|
||||
if (err.response.data.error === "User Locked") {
|
||||
createNotification({
|
||||
title: err.response.data.error,
|
||||
text: err.response.data.message,
|
||||
type: "error"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
createNotification({
|
||||
text: "Login unsuccessful. Double-check your master password and try again.",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (hasExchangedPrivateKey) {
|
||||
handleExchange();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const [captchaToken, setCaptchaToken] = useState("");
|
||||
const [shouldShowCaptcha, setShouldShowCaptcha] = useState(false);
|
||||
@@ -128,14 +210,6 @@ export const PasswordStep = ({
|
||||
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);
|
||||
@@ -183,20 +257,21 @@ export const PasswordStep = ({
|
||||
setCaptchaToken("");
|
||||
};
|
||||
|
||||
if (hasExchangedPrivateKey) {
|
||||
return (
|
||||
<div className="flex max-h-screen min-h-screen flex-col items-center justify-center gap-2 overflow-y-auto bg-gradient-to-tr from-mineshaft-600 via-mineshaft-800 to-bunker-700">
|
||||
<Spinner />
|
||||
<p className="text-white opacity-80">Loading, please wait</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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?"}
|
||||
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">
|
||||
|
||||
@@ -2,14 +2,10 @@ import crypto from "crypto";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { faInfoCircle, faXmark } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import jsrp from "jsrp";
|
||||
import nacl from "tweetnacl";
|
||||
import { encodeBase64 } from "tweetnacl-util";
|
||||
|
||||
import InputField from "@app/components/basic/InputField";
|
||||
import checkPassword from "@app/components/utilities/checks/password/checkPassword";
|
||||
import Aes256Gcm from "@app/components/utilities/cryptography/aes-256-gcm";
|
||||
import { deriveArgonKey } from "@app/components/utilities/cryptography/crypto";
|
||||
import { saveTokenToLocalStorage } from "@app/components/utilities/saveTokenToLocalStorage";
|
||||
@@ -32,17 +28,6 @@ type Props = {
|
||||
providerAuthToken?: string;
|
||||
};
|
||||
|
||||
type Errors = {
|
||||
tooShort?: string;
|
||||
tooLong?: string;
|
||||
noLetterChar?: string;
|
||||
noNumOrSpecialChar?: string;
|
||||
repeatedChar?: string;
|
||||
escapeChar?: string;
|
||||
lowEntropy?: string;
|
||||
breached?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the step of the sign up flow where people provife their name/surname and password
|
||||
* @param {object} obj
|
||||
@@ -69,12 +54,13 @@ export const UserInfoSSOStep = ({
|
||||
const [organizationName, setOrganizationName] = useState("");
|
||||
const [organizationNameError, setOrganizationNameError] = useState(false);
|
||||
const [attributionSource, setAttributionSource] = useState("");
|
||||
const [errors, setErrors] = useState<Errors>({});
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
const { mutateAsync: selectOrganization } = useSelectOrganization();
|
||||
|
||||
useEffect(() => {
|
||||
const randomPassword = crypto.randomBytes(32).toString("hex");
|
||||
setPassword(randomPassword);
|
||||
if (providerOrganizationName !== undefined) {
|
||||
setOrganizationName(providerOrganizationName);
|
||||
}
|
||||
@@ -98,11 +84,6 @@ export const UserInfoSSOStep = ({
|
||||
setOrganizationNameError(false);
|
||||
}
|
||||
|
||||
errorCheck = await checkPassword({
|
||||
password,
|
||||
setErrors
|
||||
});
|
||||
|
||||
if (!errorCheck) {
|
||||
// Generate a random pair of a public and a private key
|
||||
const pair = nacl.box.keyPair();
|
||||
@@ -158,6 +139,7 @@ export const UserInfoSSOStep = ({
|
||||
|
||||
const response = await completeAccountSignup({
|
||||
email: username,
|
||||
password,
|
||||
firstName: name.split(" ")[0],
|
||||
lastName: name.split(" ").slice(1).join(" "),
|
||||
protectedKey,
|
||||
@@ -214,6 +196,12 @@ export const UserInfoSSOStep = ({
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (password && providerOrganizationName) {
|
||||
signupErrorCheck();
|
||||
}
|
||||
}, [providerOrganizationName, password]);
|
||||
|
||||
return (
|
||||
<div className="mx-auto mb-36 h-full w-max rounded-xl md:mb-16 md:px-8">
|
||||
<p className="text-medium mx-8 mb-6 flex justify-center bg-gradient-to-b from-white to-bunker-200 bg-clip-text text-xl font-bold text-transparent md:mx-16">
|
||||
@@ -272,53 +260,6 @@ export const UserInfoSSOStep = ({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-2 flex max-h-60 w-1/4 w-full min-w-[20rem] flex-col items-center justify-center rounded-lg py-2 lg:w-1/6">
|
||||
<InputField
|
||||
label="Infisical Password"
|
||||
onChangeHandler={async (pass: string) => {
|
||||
setPassword(pass);
|
||||
await checkPassword({
|
||||
password: pass,
|
||||
setErrors
|
||||
});
|
||||
}}
|
||||
type="password"
|
||||
value={password}
|
||||
isRequired
|
||||
error={Object.keys(errors).length > 0}
|
||||
autoComplete="new-password"
|
||||
id="new-password"
|
||||
/>
|
||||
<div className="mt-2 max-h-60 w-min min-w-[20rem] flex-col items-center justify-center rounded-md bg-mineshaft-500 p-1.5 px-1.5 text-xs text-mineshaft-300">
|
||||
<FontAwesomeIcon icon={faInfoCircle} className="mr-1.5" />
|
||||
Infisical Password is used as part of the encryption mechanism so that even the
|
||||
authentication provider is not able to access your secrets.
|
||||
</div>
|
||||
{Object.keys(errors).length > 0 && (
|
||||
<div className="mt-4 flex w-full flex-col items-start rounded-md bg-white/5 px-2 py-2">
|
||||
<div className="mb-2 text-sm text-gray-400">
|
||||
{t("section.password.validate-base")}
|
||||
</div>
|
||||
{Object.keys(errors).map((key) => {
|
||||
if (errors[key as keyof Errors]) {
|
||||
return (
|
||||
<div className="items-top ml-1 flex flex-row justify-start" key={key}>
|
||||
<div>
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="text-md ml-0.5 mr-2.5 text-red"
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-gray-400">{errors[key as keyof Errors]}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mx-auto mt-2 flex w-1/4 min-w-[20rem] max-w-xs flex-col items-center justify-center text-center text-sm md:max-w-md md:text-left lg:w-[19%]">
|
||||
<div className="text-l w-full py-1 text-lg">
|
||||
<Button
|
||||
@@ -330,6 +271,7 @@ export const UserInfoSSOStep = ({
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
isLoading={isLoading}
|
||||
isDisabled={isLoading}
|
||||
>
|
||||
{" "}
|
||||
{String(t("signup.signup"))}{" "}
|
||||
|
||||
@@ -73,6 +73,7 @@ export const SignUpPage = () => {
|
||||
const { privateKey, ...userPass } = await generateUserPassKey(email, password);
|
||||
const res = await createAdminUser({
|
||||
email,
|
||||
password,
|
||||
firstName,
|
||||
lastName,
|
||||
...userPass
|
||||
|
||||
Reference in New Issue
Block a user