Add Google SSO

This commit is contained in:
Tuan Dang
2023-07-21 17:48:36 +07:00
parent 44108621b4
commit fd3cf70e13
12 changed files with 2026 additions and 1882 deletions
+1866 -1797
View File
File diff suppressed because it is too large Load Diff
+25 -1
View File
@@ -18,6 +18,30 @@ import {
getSiteURL getSiteURL
} from "../../../config"; } from "../../../config";
router.get(
"/redirect/google",
authLimiter,
passport.authenticate("google", {
scope: ["profile", "email"],
session: false,
})
);
router.get(
"/google",
passport.authenticate("google", {
failureRedirect: "/login/provider/error",
session: false
}),
async (req, res) => {
if (req.isUserCompleted) {
res.redirect(`${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`);
} else {
res.redirect(`${await getSiteURL()}/signup/sso?token=${encodeURIComponent(req.providerAuthToken)}`);
}
}
);
router.get( router.get(
"/redirect/saml2/:ssoIdentifier", "/redirect/saml2/:ssoIdentifier",
authLimiter, authLimiter,
@@ -32,7 +56,7 @@ router.post("/saml2/:ssoIdentifier",
failureFlash: true, failureFlash: true,
session: false session: false
}), }),
async (req, res) => { async (req, res) => { // TODO: move this into controller
if (req.isUserCompleted) { if (req.isUserCompleted) {
return res.redirect(`${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`); return res.redirect(`${await getSiteURL()}/login/sso?token=${encodeURIComponent(req.providerAuthToken)}`);
} }
@@ -30,6 +30,7 @@ interface FeatureSet {
customRateLimits: boolean; customRateLimits: boolean;
customAlerts: boolean; customAlerts: boolean;
auditLogs: boolean; auditLogs: boolean;
samlSSO: boolean;
status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null; status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null;
trial_end: number | null; trial_end: number | null;
has_used_trial: boolean; has_used_trial: boolean;
@@ -63,6 +64,7 @@ class EELicenseService {
customRateLimits: true, customRateLimits: true,
customAlerts: true, customAlerts: true,
auditLogs: false, auditLogs: false,
samlSSO: false,
status: null, status: null,
trial_end: null, trial_end: null,
has_used_trial: true has_used_trial: true
-16
View File
@@ -1,7 +1,6 @@
import express from "express"; import express from "express";
const router = express.Router(); const router = express.Router();
import { body } from "express-validator"; import { body } from "express-validator";
import passport from "passport";
import { requireAuth, validateRequest } from "../../middleware"; import { requireAuth, validateRequest } from "../../middleware";
import { authController } from "../../controllers/v1"; import { authController } from "../../controllers/v1";
import { authLimiter } from "../../helpers/rateLimiter"; import { authLimiter } from "../../helpers/rateLimiter";
@@ -44,21 +43,6 @@ router.post(
authController.checkAuth authController.checkAuth
); );
router.get(
"/redirect/google",
authLimiter,
passport.authenticate("google", {
scope: ["profile", "email"],
session: false,
}),
);
router.get(
"/callback/google",
passport.authenticate("google", { failureRedirect: "/login/provider/error", session: false }),
authController.handleAuthProviderCallback,
);
router.get( router.get(
"/common-passwords", "/common-passwords",
authLimiter, authLimiter,
+33 -16
View File
@@ -18,16 +18,15 @@ import {
getJwtProviderAuthSecret, getJwtProviderAuthSecret,
} from "../config"; } from "../config";
import { getSSOConfigHelper } from "../ee/helpers/organizations"; import { getSSOConfigHelper } from "../ee/helpers/organizations";
import { OrganizationNotFoundError } from "./errors"; import { OrganizationNotFoundError, InternalServerError } from "./errors";
import { MEMBER, INVITED } from "../variables"; import { MEMBER, INVITED } from "../variables";
import { getSiteURL } from "../config";
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const GoogleStrategy = require("passport-google-oauth20").Strategy; const GoogleStrategy = require("passport-google-oauth20").Strategy;
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
const { MultiSamlStrategy } = require("@node-saml/passport-saml"); const { MultiSamlStrategy } = require("@node-saml/passport-saml");
// TODO: find a more optimal folder structure to store these types of functions
/** /**
* Returns an object containing the id of the authentication data payload * Returns an object containing the id of the authentication data payload
* @param {AuthData} authData - authentication data object * @param {AuthData} authData - authentication data object
@@ -75,45 +74,57 @@ const initializePassport = async () => {
passReqToCallback: true, passReqToCallback: true,
clientID: googleClientId, clientID: googleClientId,
clientSecret: googleClientSecret, clientSecret: googleClientSecret,
callbackURL: "/api/v1/auth/callback/google", callbackURL: "/api/v1/sso/google",
scope: ["profile", " email"], scope: ["profile", " email"],
}, async ( }, async (
req: express.Request, req: express.Request,
accessToken: string, accessToken: string,
refreshToken: string, refreshToken: string,
profile: any, profile: any,
cb: any done: any
) => { ) => {
try { try {
const email = profile.emails[0].value; const email = profile.emails[0].value;
const firstName = profile.name.givenName;
const lastName = profile.name.familyName;
let user = await User.findOne({ let user = await User.findOne({
authProvider: AuthProvider.GOOGLE, email
authId: profile.id, }).select("+publicKey");
}).select("+publicKey")
if (user && user.authProvider !== AuthProvider.GOOGLE) {
done(InternalServerError());
}
if (!user) { if (!user) {
user = await new User({ user = await new User({
email, email,
authProvider: AuthProvider.GOOGLE, authProvider: AuthProvider.GOOGLE,
authId: profile.id, authId: profile.id,
firstName,
lastName
}).save(); }).save();
} }
const isUserCompleted = !!user.publicKey;
const providerAuthToken = createToken({ const providerAuthToken = createToken({
payload: { payload: {
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName,
lastName,
authProvider: user.authProvider, authProvider: user.authProvider,
isUserCompleted: !!user.publicKey, isUserCompleted
}, },
expiresIn: await getJwtProviderAuthLifetime(), expiresIn: await getJwtProviderAuthLifetime(),
secret: await getJwtProviderAuthSecret(), secret: await getJwtProviderAuthSecret(),
}); });
req.isUserCompleted = isUserCompleted;
req.providerAuthToken = providerAuthToken; req.providerAuthToken = providerAuthToken;
cb(null, profile); done(null, profile);
} catch (err) { } catch (err) {
cb(null, false); done(null, false);
} }
})); }));
@@ -129,7 +140,7 @@ const initializePassport = async () => {
const samlConfig = ({ const samlConfig = ({
path: "/api/v1/auth/callback/saml", path: "/api/v1/auth/callback/saml",
callbackURL: "http://localhost:8080/api/v1/auth/callback/saml", // TODO: get rid of localhost:8080 here callbackURL: `${await getSiteURL()}/api/v1/auth/callback/saml`,
entryPoint: ssoConfig.entryPoint, entryPoint: ssoConfig.entryPoint,
issuer: ssoConfig.issuer, issuer: ssoConfig.issuer,
cert: ssoConfig.cert, cert: ssoConfig.cert,
@@ -143,23 +154,29 @@ const initializePassport = async () => {
}, },
async (req: any, profile: any, done: any) => { async (req: any, profile: any, done: any) => {
if (!req.ssoConfig.isActive) return done(InternalServerError());
const organization = await Organization.findById(req.ssoConfig.organization); const organization = await Organization.findById(req.ssoConfig.organization);
if (!organization) done(OrganizationNotFoundError()); if (!organization) return done(OrganizationNotFoundError());
const email = profile.email; const email = profile.email;
const firstName = profile.firstName; const firstName = profile.firstName;
const lastName = profile.lastName; const lastName = profile.lastName;
let user = await User.findOne({ let user = await User.findOne({
authProvider: AuthProvider.OKTA_SAML,
email email
}).select("+publicKey"); }).select("+publicKey");
if (user && user.authProvider !== AuthProvider.OKTA_SAML) {
done(InternalServerError());
}
if (!user) { if (!user) {
user = await new User({ user = await new User({
email, email,
authProvider: AuthProvider.OKTA_SAML, authProvider: AuthProvider.OKTA_SAML,
authId: profile.id,
firstName, firstName,
lastName lastName
}).save(); }).save();
@@ -178,8 +195,8 @@ const initializePassport = async () => {
payload: { payload: {
userId: user._id.toString(), userId: user._id.toString(),
email: user.email, email: user.email,
firstName: user.firstName, firstName,
lastName: user.lastName, lastName,
organizationName: organization?.name, organizationName: organization?.name,
authProvider: user.authProvider, authProvider: user.authProvider,
isUserCompleted isUserCompleted
@@ -1,9 +1,8 @@
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 { faGoogle } from '@fortawesome/free-brands-svg-icons';
// import { faGoogle } from '@fortawesome/free-brands-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button } from "../v2"; import { Button } from "../v2";
export default function InitialSignupStep({ export default function InitialSignupStep({
@@ -16,19 +15,6 @@ export default function InitialSignupStep({
return <div className='flex flex-col mx-auto w-full justify-center items-center'> return <div className='flex flex-col mx-auto w-full justify-center items-center'>
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >{t("signup.initial-title")}</h1> <h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >{t("signup.initial-title")}</h1>
{/* <div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'>
<Button
colorSchema="primary"
variant="solid"
onClick={() => {
window.open('/api/v1/auth/redirect/google')
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-14 w-full mx-0"
>
{t('signup.continue-with-google')}
</Button>
</div> */}
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md'> <div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md'>
<Button <Button
colorSchema="primary" colorSchema="primary"
@@ -42,6 +28,25 @@ export default function InitialSignupStep({
Sign Up with email Sign Up with email
</Button> </Button>
</div> </div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-1/2 border-t border-mineshaft-500' />
<span className='px-4 text-sm text-bunker-400'>or</span>
<div className='w-1/2 border-t border-mineshaft-500' />
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'>
<Button
colorSchema="primary"
variant="solid"
onClick={() => {
window.open('/api/v1/sso/redirect/google');
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-14 w-full mx-0"
>
{t('signup.continue-with-google')}
</Button>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'> <div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'>
<Button <Button
colorSchema="primary" colorSchema="primary"
@@ -13,6 +13,7 @@ export type SubscriptionPlan = {
workspaceLimit: number; workspaceLimit: number;
workspacesUsed: number; workspacesUsed: number;
environmentLimit: number; environmentLimit: number;
samlSSO: boolean;
status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null; status: "incomplete" | "incomplete_expired" | "trialing" | "active" | "past_due" | "canceled" | "unpaid" | null;
trial_end: number | null; trial_end: number | null;
has_used_trial: boolean; has_used_trial: boolean;
@@ -2,8 +2,8 @@ import { FormEvent, 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 { faGoogle } from "@fortawesome/free-brands-svg-icons"; import { faGoogle } from "@fortawesome/free-brands-svg-icons";
// import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import axios from "axios" import axios from "axios"
import Error from "@app/components/basic/Error"; import Error from "@app/components/basic/Error";
@@ -115,20 +115,6 @@ export const InitialStep = ({
return ( return (
<form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'> <form onSubmit={handleLogin} className='flex flex-col mx-auto w-full justify-center items-center'>
<h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1> <h1 className='text-xl font-medium text-transparent bg-clip-text bg-gradient-to-b from-white to-bunker-200 text-center mb-8' >Login to Infisical</h1>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'>
{/* <Button
colorSchema="primary"
variant="solid"
onClick={() => {
// window.open('/api/v1/auth/redirect/google')
window.open('/api/v1/auth/redirect/okta/64b4b9166e76604655b5373e');
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-14 w-full mx-0"
>
{t('login.continue-with-google')}
</Button> */}
</div>
<div className="relative md:px-1.5 flex items-center justify-center lg:w-1/6 w-1/4 min-w-[21.3rem] md:min-w-[22rem] mx-auto rounded-lg max-h-24 md:max-h-28"> <div className="relative md:px-1.5 flex items-center justify-center lg:w-1/6 w-1/4 min-w-[21.3rem] md:min-w-[22rem] mx-auto rounded-lg max-h-24 md:max-h-28">
<div className="flex items-center justify-center w-full md:px-2 md:py-1 rounded-lg max-h-24 md:max-h-28"> <div className="flex items-center justify-center w-full md:px-2 md:py-1 rounded-lg max-h-24 md:max-h-28">
<Input <Input
@@ -168,11 +154,25 @@ export const InitialStep = ({
isLoading={isLoading} isLoading={isLoading}
> Login </Button> > Login </Button>
</div> </div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center mt-4 py-2'> <div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center my-4 py-2'>
<div className='w-1/2 border-t border-mineshaft-500' /> <div className='w-1/2 border-t border-mineshaft-500' />
<span className='px-4 text-sm text-bunker-400'>or</span> <span className='px-4 text-sm text-bunker-400'>or</span>
<div className='w-1/2 border-t border-mineshaft-500' /> <div className='w-1/2 border-t border-mineshaft-500' />
</div> </div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] rounded-md'>
<Button
colorSchema="primary"
variant="solid"
onClick={() => {
window.open('/api/v1/sso/redirect/google');
window.close();
}}
leftIcon={<FontAwesomeIcon icon={faGoogle} className="mr-1" />}
className="h-14 w-full mx-0"
>
{t('login.continue-with-google')}
</Button>
</div>
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'> <div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'>
<Button <Button
colorSchema="primary" colorSchema="primary"
@@ -1,15 +1,13 @@
import { faPlus } from "@fortawesome/free-solid-svg-icons"; import { faPlus } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider"; import { useNotificationContext } from "@app/components/context/Notifications/NotificationProvider";
import { Button , Switch } from "@app/components/v2"; import { Button, Switch, UpgradePlanModal } from "@app/components/v2";
import { useOrganization } from "@app/context"; import { useOrganization, useSubscription } from "@app/context";
import { import {
useGetSSOConfig, useGetSSOConfig,
useUpdateSSOConfig useUpdateSSOConfig
} from "@app/hooks/api"; } from "@app/hooks/api";
import { usePopUp } from "@app/hooks/usePopUp"; import { usePopUp } from "@app/hooks/usePopUp";
import { SSOModal } from "./SSOModal"; import { SSOModal } from "./SSOModal";
const ssoAuthProviderMap: { [key: string]: string } = { const ssoAuthProviderMap: { [key: string]: string } = {
@@ -18,10 +16,12 @@ const ssoAuthProviderMap: { [key: string]: string } = {
export const OrgSSOSection = (): JSX.Element => { export const OrgSSOSection = (): JSX.Element => {
const { currentOrg } = useOrganization(); const { currentOrg } = useOrganization();
const { subscription } = useSubscription();
const { createNotification } = useNotificationContext(); const { createNotification } = useNotificationContext();
const { data, isLoading } = useGetSSOConfig(currentOrg?._id ?? ""); const { data, isLoading } = useGetSSOConfig(currentOrg?._id ?? "");
const { mutateAsync } = useUpdateSSOConfig(); const { mutateAsync } = useUpdateSSOConfig();
const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([ const { popUp, handlePopUpOpen, handlePopUpClose, handlePopUpToggle } = usePopUp([
"upgradePlan",
"addSSO" "addSSO"
] as const); ] as const);
@@ -55,7 +55,13 @@ export const OrgSSOSection = (): JSX.Element => {
</h2> </h2>
{!isLoading && ( {!isLoading && (
<Button <Button
onClick={() => handlePopUpOpen("addSSO")} onClick={() => {
if (subscription?.samlSSO) {
handlePopUpOpen("addSSO");
} else {
handlePopUpOpen("upgradePlan");
}
}}
colorSchema="secondary" colorSchema="secondary"
leftIcon={<FontAwesomeIcon icon={faPlus} />} leftIcon={<FontAwesomeIcon icon={faPlus} />}
> >
@@ -101,6 +107,11 @@ export const OrgSSOSection = (): JSX.Element => {
handlePopUpClose={handlePopUpClose} handlePopUpClose={handlePopUpClose}
handlePopUpToggle={handlePopUpToggle} handlePopUpToggle={handlePopUpToggle}
/> />
<UpgradePlanModal
isOpen={popUp.upgradePlan.isOpen}
onOpenChange={(isOpen) => handlePopUpToggle("upgradePlan", isOpen)}
text="You can use SAML SSO if you switch to Infisical's Pro plan."
/>
</div> </div>
); );
}; };
@@ -1,15 +1,31 @@
import { Fragment } from "react" import { Fragment } from "react"
import { Tab } from "@headlessui/react" import { Tab } from "@headlessui/react"
import { OrgAuthTab } from "../OrgAuthTab"; import { OrgAuthTab } from "../OrgAuthTab";
import { OrgGeneralTab } from "../OrgGeneralTab"; import { OrgGeneralTab } from "../OrgGeneralTab";
import { useUser, useOrganization } from "@app/context";
const tabs = [ import {
{ name: "General", key: "tab-org-general" }, useGetOrgUsers
{ name: "SAML SSO", key: "tab-org-saml" } } from "@app/hooks/api";
];
export const OrgTabGroup = () => { export const OrgTabGroup = () => {
const { currentOrg } = useOrganization();
const { user } = useUser();
const { data } = useGetOrgUsers(currentOrg?._id ?? "");
const isRoleSufficient = data?.some((orgUser) => {
return orgUser.role !== 'member' && orgUser.user._id === user._id;
});
const tabs = [
{ name: "General", key: "tab-org-general" },
];
if (isRoleSufficient) {
tabs.push(
{ name: "SAML SSO", key: "tab-org-saml" }
);
}
return ( return (
<Tab.Group> <Tab.Group>
<Tab.List className="mb-6 border-b-2 border-mineshaft-800 w-full"> <Tab.List className="mb-6 border-b-2 border-mineshaft-800 w-full">
@@ -30,9 +46,11 @@ export const OrgTabGroup = () => {
<Tab.Panel> <Tab.Panel>
<OrgGeneralTab /> <OrgGeneralTab />
</Tab.Panel> </Tab.Panel>
<Tab.Panel> {isRoleSufficient && (
<OrgAuthTab /> <Tab.Panel>
</Tab.Panel> <OrgAuthTab />
</Tab.Panel>
)}
</Tab.Panels> </Tab.Panels>
</Tab.Group> </Tab.Group>
); );
+1 -1
View File
@@ -29,7 +29,7 @@ export const SignupSSO = ({
<UserInfoSSOStep <UserInfoSSOStep
email={email} email={email}
name={`${firstName} ${lastName}`} name={`${firstName} ${lastName}`}
organizationName={organizationName} providerOrganizationName={organizationName}
password={password} password={password}
setPassword={setPassword} setPassword={setPassword}
setStep={setStep} setStep={setStep}
@@ -1,7 +1,7 @@
import crypto from "crypto"; import crypto from "crypto";
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { faXmark } from "@fortawesome/free-solid-svg-icons"; import { faXmark } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
@@ -30,7 +30,7 @@ type Props = {
password: string; password: string;
setPassword: (value: string) => void; setPassword: (value: string) => void;
name: string; name: string;
organizationName: string; providerOrganizationName: string;
providerAuthToken?: string; providerAuthToken?: string;
} }
@@ -59,7 +59,7 @@ type Errors = {
export const UserInfoSSOStep = ({ export const UserInfoSSOStep = ({
email, email,
name, name,
organizationName, providerOrganizationName,
password, password,
setPassword, setPassword,
setStep, setStep,
@@ -67,11 +67,19 @@ export const UserInfoSSOStep = ({
}: Props) => { }: Props) => {
const { data: commonPasswords } = useGetCommonPasswords(); const { data: commonPasswords } = useGetCommonPasswords();
const [nameError, setNameError] = useState(false); const [nameError, setNameError] = useState(false);
const [organizationName, setOrganizationName] = useState("");
const [organizationNameError, setOrganizationNameError] = useState(false); const [organizationNameError, setOrganizationNameError] = useState(false);
const [errors, setErrors] = useState<Errors>({}); const [errors, setErrors] = useState<Errors>({});
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation(); const { t } = useTranslation();
useEffect(() => {
console.log("providerOrganizationName: ", providerOrganizationName);
if (providerOrganizationName) {
setOrganizationName(providerOrganizationName);
}
}, []);
// Verifies if the information that the users entered (name, workspace) // Verifies if the information that the users entered (name, workspace)
// is there, and if the password matches the criteria. // is there, and if the password matches the criteria.
const signupErrorCheck = async () => { const signupErrorCheck = async () => {
@@ -105,6 +113,10 @@ export const UserInfoSSOStep = ({
const publicKey = encodeBase64(publicKeyUint8Array); const publicKey = encodeBase64(publicKeyUint8Array);
localStorage.setItem("PRIVATE_KEY", privateKey); localStorage.setItem("PRIVATE_KEY", privateKey);
console.log("make");
console.log("email: ", email);
console.log("password: ", password);
client.init( client.init(
{ {
username: email, username: email,
@@ -225,6 +237,7 @@ export const UserInfoSSOStep = ({
<Input <Input
placeholder="Infisical" placeholder="Infisical"
value={organizationName} value={organizationName}
onChange={(e) => setOrganizationName(e.target.value)}
isRequired isRequired
className="h-12" className="h-12"
disabled disabled