diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go index 261a2883b..4717e0784 100644 --- a/cli/packages/cmd/login.go +++ b/cli/packages/cmd/login.go @@ -24,7 +24,6 @@ import ( "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/srp" "github.com/Infisical/infisical-merge/packages/util" - "github.com/chzyer/readline" "github.com/fatih/color" "github.com/go-resty/resty/v2" "github.com/manifoldco/promptui" @@ -205,6 +204,7 @@ var loginCmd = &cobra.Command{ if !overrideDomain { domainQuery = false config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL_MANUAL_OVERRIDE) + config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", strings.TrimSuffix(config.INFISICAL_URL, "/api")) } } @@ -713,7 +713,7 @@ func askForMFACode() string { return mfaVerifyCode } -func askToPasteJwtToken(stdin *readline.CancelableStdin, success chan models.UserCredentials, failure chan error) { +func askToPasteJwtToken(success chan models.UserCredentials, failure chan error) { time.Sleep(time.Second * 5) fmt.Println("\n\nOnce login is completed via browser, the CLI should be authenticated automatically.") fmt.Println("However, if browser fails to communicate with the CLI, please paste the token from the browser below.") @@ -807,26 +807,22 @@ func browserCliLogin() (models.UserCredentials, error) { log.Debug().Msgf("Callback server listening on port %d", callbackPort) - stdin := readline.NewCancelableStdin(os.Stdin) go http.Serve(listener, corsHandler) - go askToPasteJwtToken(stdin, success, failure) + go askToPasteJwtToken(success, failure) for { select { case loginResponse := <-success: _ = closeListener(&listener) - _ = stdin.Close() fmt.Println("Browser login successful") return loginResponse, nil case err := <-failure: serverErr := closeListener(&listener) - stdErr := stdin.Close() - return models.UserCredentials{}, errors.Join(err, serverErr, stdErr) + return models.UserCredentials{}, errors.Join(err, serverErr) case <-timeout: _ = closeListener(&listener) - _ = stdin.Close() return models.UserCredentials{}, errors.New("server timeout") } } diff --git a/frontend/src/views/Login/components/MFAStep/MFAStep.tsx b/frontend/src/views/Login/components/MFAStep/MFAStep.tsx index 70f3eee25..a6235e9d5 100644 --- a/frontend/src/views/Login/components/MFAStep/MFAStep.tsx +++ b/frontend/src/views/Login/components/MFAStep/MFAStep.tsx @@ -168,16 +168,27 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => { const { token: newJwtToken } = await selectOrganization({ organizationId }); const instance = axios.create(); - await instance.post(cliUrl, { + const payload = { ...isCliLoginSuccessful.loginResponse, JTWToken: newJwtToken + }; + await instance.post(cliUrl, payload).catch(() => { + // if error happens to communicate we set the token with an expiry in sessino storage + // the cli-redirect page has logic to show this to user and ask them to paste it in terminal + sessionStorage.setItem( + SessionStorageKeys.CLI_TERMINAL_TOKEN, + JSON.stringify({ + expiry: formatISO(addSeconds(new Date(), 30)), + data: window.btoa(JSON.stringify(payload)) + }) + ); }); - - await navigateUserToOrg(router, organizationId); + router.push("/cli-redirect"); + return; } // 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 @@ -189,7 +200,7 @@ export const MFAStep = ({ email, password, providerAuthToken }: Props) => { else { await navigateUserToOrg(router); } - } + } } else { const isLoginSuccessful = await attemptLoginMfa({ diff --git a/frontend/src/views/Login/components/PasswordStep/PasswordStep.tsx b/frontend/src/views/Login/components/PasswordStep/PasswordStep.tsx index 10be08f4c..16a06f9b7 100644 --- a/frontend/src/views/Login/components/PasswordStep/PasswordStep.tsx +++ b/frontend/src/views/Login/components/PasswordStep/PasswordStep.tsx @@ -4,6 +4,7 @@ import Link from "next/link"; import { useRouter } from "next/router"; import HCaptcha from "@hcaptcha/react-hcaptcha"; import axios from "axios"; +import { addSeconds, formatISO } from "date-fns"; import jwt_decode from "jwt-decode"; import { createNotification } from "@app/components/notifications"; @@ -12,6 +13,7 @@ import attemptLogin from "@app/components/utilities/attemptLogin"; import { CAPTCHA_SITE_KEY } from "@app/components/utilities/config"; import SecurityClient from "@app/components/utilities/SecurityClient"; import { Button, Input, Spinner } from "@app/components/v2"; +import { SessionStorageKeys } from "@app/const"; import { useOauthTokenExchange, useSelectOrganization } from "@app/hooks/api"; import { fetchOrganizations } from "@app/hooks/api/organization/queries"; import { fetchMyPrivateKey } from "@app/hooks/api/users/queries"; @@ -79,11 +81,24 @@ export const PasswordStep = ({ 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, { + const payload = { privateKey, email, JTWToken: newJwtToken + }; + await instance.post(cliUrl, payload).catch(() => { + // if error happens to communicate we set the token with an expiry in sessino storage + // the cli-redirect page has logic to show this to user and ask them to paste it in terminal + sessionStorage.setItem( + SessionStorageKeys.CLI_TERMINAL_TOKEN, + JSON.stringify({ + expiry: formatISO(addSeconds(new Date(), 30)), + data: window.btoa(JSON.stringify(payload)) + }) + ); }); + router.push("/cli-redirect"); + return; } await navigateUserToOrg(router, organizationId); @@ -165,26 +180,35 @@ export const PasswordStep = ({ ); const instance = axios.create(); - await instance.post(cliUrl, { + const payload = { ...isCliLoginSuccessful.loginResponse, JTWToken: newJwtToken + }; + await instance.post(cliUrl, payload).catch(() => { + // if error happens to communicate we set the token with an expiry in sessino storage + // the cli-redirect page has logic to show this to user and ask them to paste it in terminal + sessionStorage.setItem( + SessionStorageKeys.CLI_TERMINAL_TOKEN, + JSON.stringify({ + expiry: formatISO(addSeconds(new Date(), 30)), + data: window.btoa(JSON.stringify(payload)) + }) + ); }); - - await navigateUserToOrg(router, organizationId); + router.push("/cli-redirect"); + return; } // 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(); + const userOrgs = await fetchOrganizations(); - // case: user has orgs, so we navigate the user to select an org - if (userOrgs.length > 0) { - navigateToSelectOrganization(callbackPort); - } - // case: no orgs found, so we navigate the user to create an org - else { - await navigateUserToOrg(router); - } + // case: user has orgs, so we navigate the user to select an org + if (userOrgs.length > 0) { + navigateToSelectOrganization(callbackPort); + } + // case: no orgs found, so we navigate the user to create an org + else { + await navigateUserToOrg(router); } } } else {