mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
feat: cli login via browser
This commit is contained in:
@@ -5,6 +5,7 @@ go 1.19
|
||||
require (
|
||||
github.com/99designs/keyring v1.2.2
|
||||
github.com/charmbracelet/lipgloss v0.5.0
|
||||
github.com/denisbrodbeck/machineid v1.0.1
|
||||
github.com/fatih/semgroup v1.2.0
|
||||
github.com/gitleaks/go-gitdiff v0.8.0
|
||||
github.com/h2non/filetype v1.1.3
|
||||
@@ -14,6 +15,7 @@ require (
|
||||
github.com/muesli/reflow v0.3.0
|
||||
github.com/muesli/roff v0.1.0
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9
|
||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a
|
||||
github.com/rs/zerolog v1.26.1
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/spf13/viper v1.8.1
|
||||
@@ -28,7 +30,6 @@ require (
|
||||
github.com/chzyer/readline v1.5.1 // indirect
|
||||
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denisbrodbeck/machineid v1.0.1 // indirect
|
||||
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
@@ -47,9 +48,10 @@ require (
|
||||
github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/pelletier/go-toml v1.9.3 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/posthog/posthog-go v0.0.0-20221221115252-24dfed35d71a // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/cors v1.9.0 // indirect
|
||||
github.com/spf13/afero v1.6.0 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
|
||||
@@ -283,6 +283,8 @@ github.com/pelletier/go-toml v1.9.3 h1:zeC5b1GviRUyKYd6OJPvBU/mcVDVoL1OhT17FCt5d
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9 h1:lL+y4Xv20pVlCGyLzNHRC0I0rIHhIL1lTvHizoS/dU8=
|
||||
github.com/petar-dambovaliev/aho-corasick v0.0.0-20211021192214-5ab2d9280aa9/go.mod h1:EHPiTAKtiFmrMldLUNswFwfZ2eJIYBHktdaUTZxYWRw=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
@@ -297,6 +299,8 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||
github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
|
||||
github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
|
||||
@@ -520,6 +524,7 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -6,10 +6,14 @@ package cmd
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
||||
@@ -22,7 +26,9 @@ import (
|
||||
"github.com/fatih/color"
|
||||
"github.com/go-resty/resty/v2"
|
||||
"github.com/manifoldco/promptui"
|
||||
"github.com/pkg/browser"
|
||||
"github.com/posthog/posthog-go"
|
||||
"github.com/rs/cors"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/crypto/argon2"
|
||||
@@ -90,16 +96,43 @@ var loginCmd = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
email, password, err := askForLoginCredentials()
|
||||
var (
|
||||
email string
|
||||
password string
|
||||
loginOneResponse *api.GetLoginOneV2Response
|
||||
loginTwoResponse *api.GetLoginTwoV2Response
|
||||
)
|
||||
|
||||
//check for --browser flag
|
||||
browserLogin, err := cmd.Flags().GetBool("browser")
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse email and password for authentication")
|
||||
util.HandleError(err, "Unable to parse browser flag")
|
||||
}
|
||||
|
||||
loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to authenticate with the provided credentials, please try again")
|
||||
log.Debug().Err(err)
|
||||
return
|
||||
if browserLogin {
|
||||
log.Debug().Msg("Login via browser")
|
||||
//call browser login function
|
||||
loginResponse, err := browserCliLogin()
|
||||
if err != nil {
|
||||
util.HandleError(err, err.Error())
|
||||
}
|
||||
email = loginResponse.Email
|
||||
password = loginResponse.Password
|
||||
loginOneResponse = &loginResponse.LoginOneResponse
|
||||
loginTwoResponse = &loginResponse.LoginTwoResponse
|
||||
//util.HandleError(errors.New("not implemented"), " login via browser not fully implemented")
|
||||
} else {
|
||||
email, password, err = askForLoginCredentials()
|
||||
if err != nil {
|
||||
util.HandleError(err, "Unable to parse email and password for authentication")
|
||||
}
|
||||
|
||||
loginOneResponse, loginTwoResponse, err = getFreshUserCredentials(email, password)
|
||||
if err != nil {
|
||||
fmt.Println("Unable to authenticate with the provided credentials, please try again")
|
||||
log.Debug().Err(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if loginTwoResponse.MfaEnabled {
|
||||
@@ -282,6 +315,7 @@ var loginCmd = &cobra.Command{
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(loginCmd)
|
||||
loginCmd.Flags().BoolP("browser", "b", false, "Login via browser")
|
||||
}
|
||||
|
||||
func DomainOverridePrompt() (bool, error) {
|
||||
@@ -326,7 +360,8 @@ func askForDomain() error {
|
||||
|
||||
if selectedHostingOption == INFISICAL_CLOUD {
|
||||
//cloud option
|
||||
config.INFISICAL_URL = util.INFISICAL_DEFAULT_API_URL
|
||||
config.INFISICAL_URL = fmt.Sprintf("%s/api", util.INFISICAL_DEFAULT_URL)
|
||||
config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", util.INFISICAL_DEFAULT_URL)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -341,7 +376,7 @@ func askForDomain() error {
|
||||
domainPrompt := promptui.Prompt{
|
||||
Label: "Domain",
|
||||
Validate: urlValidation,
|
||||
Default: "Example - https://my-self-hosted-instance.com/api",
|
||||
Default: "Example - https://my-self-hosted-instance.com",
|
||||
}
|
||||
|
||||
domain, err := domainPrompt.Run()
|
||||
@@ -349,8 +384,9 @@ func askForDomain() error {
|
||||
return err
|
||||
}
|
||||
|
||||
//set api url
|
||||
config.INFISICAL_URL = domain
|
||||
//set api and login url
|
||||
config.INFISICAL_URL = fmt.Sprintf("%s/api", domain)
|
||||
config.INFISICAL_LOGIN_URL = fmt.Sprintf("%s/login", domain)
|
||||
//return nil
|
||||
return nil
|
||||
}
|
||||
@@ -476,3 +512,91 @@ func askForMFACode() string {
|
||||
|
||||
return mfaVerifyCode
|
||||
}
|
||||
|
||||
type CliLoginResponse struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
LoginOneResponse api.GetLoginOneV2Response `json:"loginOneResponse"`
|
||||
LoginTwoResponse api.GetLoginTwoV2Response `json:"loginTwoResponse"`
|
||||
}
|
||||
|
||||
// Manages the browser login flow
|
||||
// returns a CLILoginResponse on success and an error on failure
|
||||
func browserCliLogin() (CliLoginResponse, error) {
|
||||
|
||||
//create listener
|
||||
listener, err := net.Listen("tcp", ":0")
|
||||
if err != nil {
|
||||
return CliLoginResponse{}, err
|
||||
}
|
||||
|
||||
//get callback port
|
||||
callbackPort := listener.Addr().(*net.TCPAddr).Port
|
||||
url := fmt.Sprintf("%s?callback_port=%d", config.INFISICAL_LOGIN_URL, callbackPort)
|
||||
|
||||
//open browser and login
|
||||
err = browser.OpenURL(url)
|
||||
if err != nil {
|
||||
return CliLoginResponse{}, err
|
||||
}
|
||||
|
||||
//flow channels
|
||||
success := make(chan CliLoginResponse)
|
||||
failure := make(chan error)
|
||||
timeout := time.After(time.Second * 60)
|
||||
|
||||
//create handler
|
||||
c := cors.New(cors.Options{
|
||||
AllowedOrigins: []string{strings.ReplaceAll(config.INFISICAL_LOGIN_URL, "/login", "")},
|
||||
AllowCredentials: true,
|
||||
AllowedMethods: []string{"POST", "OPTIONS"},
|
||||
AllowedHeaders: []string{"Content-Type"},
|
||||
Debug: false,
|
||||
})
|
||||
corsHandler := c.Handler(browserLoginHandler(success, failure))
|
||||
|
||||
log.Debug().Msgf("Callback server listening on port %d", callbackPort)
|
||||
go http.Serve(listener, corsHandler)
|
||||
|
||||
for {
|
||||
select {
|
||||
case loginResponse := <-success:
|
||||
err = closeListener(&listener)
|
||||
return loginResponse, nil
|
||||
|
||||
case err = <-failure:
|
||||
err = closeListener(&listener)
|
||||
return CliLoginResponse{}, err
|
||||
|
||||
case _ = <-timeout:
|
||||
err = closeListener(&listener)
|
||||
return CliLoginResponse{}, errors.New("server timeout")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func closeListener(listener *net.Listener) error {
|
||||
err := (*listener).Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Debug().Msg("Callback server shutdown successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func browserLoginHandler(success chan CliLoginResponse, failure chan error) http.HandlerFunc {
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var loginResponse CliLoginResponse
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&loginResponse)
|
||||
if err != nil {
|
||||
failure <- err
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
success <- loginResponse
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,3 +2,4 @@ package config
|
||||
|
||||
var INFISICAL_URL string
|
||||
var INFISICAL_URL_MANUAL_OVERRIDE string
|
||||
var INFISICAL_LOGIN_URL string
|
||||
|
||||
@@ -4,6 +4,7 @@ const (
|
||||
CONFIG_FILE_NAME = "infisical-config.json"
|
||||
CONFIG_FOLDER_NAME = ".infisical"
|
||||
INFISICAL_DEFAULT_API_URL = "https://app.infisical.com/api"
|
||||
INFISICAL_DEFAULT_URL = "https://app.infisical.com"
|
||||
INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
|
||||
INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
|
||||
SECRET_TYPE_PERSONAL = "personal"
|
||||
|
||||
@@ -11,7 +11,7 @@ const ContentSecurityPolicy = `
|
||||
style-src 'self' https://rsms.me 'unsafe-inline';
|
||||
child-src https://api.stripe.com;
|
||||
frame-src https://js.stripe.com/ https://api.stripe.com;
|
||||
connect-src 'self' https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com;
|
||||
connect-src 'self' https://api.heroku.com/ https://id.heroku.com/oauth/authorize https://id.heroku.com/oauth/token https://checkout.stripe.com https://app.posthog.com https://api.stripe.com http://localhost:*;
|
||||
img-src 'self' https://*.stripe.com https://i.ytimg.com/ data:;
|
||||
media-src;
|
||||
font-src 'self' https://maxcdn.bootstrapcdn.com https://rsms.me https://fonts.gstatic.com;
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/router';
|
||||
import axios from "axios"
|
||||
|
||||
import attemptLogin from '@app/components/utilities/attemptLogin';
|
||||
|
||||
@@ -9,6 +10,7 @@ import Error from '../basic/Error';
|
||||
// import { faGoogle } from '@fortawesome/free-brands-svg-icons';
|
||||
// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { Button, Input } from '../v2';
|
||||
import attemptCliLogin from '../utilities/attemptCliLogin';
|
||||
|
||||
export default function InitialLoginStep({
|
||||
setStep,
|
||||
@@ -31,33 +33,60 @@ export default function InitialLoginStep({
|
||||
|
||||
const handleLogin = async () => {
|
||||
try {
|
||||
if (!email || !password) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoading(true);
|
||||
const isLoginSuccessful = await attemptLogin({
|
||||
email,
|
||||
password,
|
||||
});
|
||||
if (isLoginSuccessful && isLoginSuccessful.success) {
|
||||
// case: login was successful
|
||||
|
||||
if (isLoginSuccessful.mfaEnabled) {
|
||||
// case: login requires MFA step
|
||||
setStep(2);
|
||||
setIsLoading(false);
|
||||
return;
|
||||
if (!email || !password) {
|
||||
return;
|
||||
}
|
||||
|
||||
// case: login does not require MFA step
|
||||
router.push(`/dashboard/${localStorage.getItem('projectData.id')}`);
|
||||
}
|
||||
|
||||
|
||||
setIsLoading(true);
|
||||
const queryParams = new URLSearchParams(location.search)
|
||||
if (queryParams) {
|
||||
const callbackPort = queryParams.get("callback_port")
|
||||
|
||||
//attemptCliLogin
|
||||
const isCliLoginSuccessful = await attemptCliLogin({
|
||||
email,
|
||||
password,
|
||||
})
|
||||
|
||||
if (isCliLoginSuccessful && isCliLoginSuccessful.success) {
|
||||
// case: login was successful
|
||||
const cliUrl = `http://localhost:${callbackPort}`
|
||||
|
||||
//send request to server endpoint
|
||||
const instance = axios.create()
|
||||
const cliResp = await instance.post(cliUrl,{...isCliLoginSuccessful.loginResponse,email,password})
|
||||
|
||||
//cli page
|
||||
router.push("/cli-redirect");
|
||||
|
||||
//on success, router.push to cli Login Successful page
|
||||
|
||||
}
|
||||
} else {
|
||||
const isLoginSuccessful = await attemptLogin({
|
||||
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);
|
||||
setLoginError(true);
|
||||
}
|
||||
|
||||
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -90,18 +119,18 @@ export default function InitialLoginStep({
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative pt-2 md:pt-0 md:px-1.5 flex items-center justify-center lg:w-1/6 w-1/4 min-w-[20rem] md:min-w-[22rem] mx-auto w-full rounded-lg max-h-24 md:max-h-28">
|
||||
<div className="flex items-center justify-center w-full md:p-2 rounded-lg max-h-24 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 select:-webkit-autofill:focus"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center justify-center w-full md:p-2 rounded-lg max-h-24 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 select:-webkit-autofill:focus"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{!isLoading && loginError && <Error text={t('login.error-login') ?? ''} />}
|
||||
<div className='lg:w-1/6 w-1/4 min-w-[21.2rem] md:min-w-[20.1rem] text-center rounded-md mt-4'>
|
||||
@@ -116,18 +145,18 @@ export default function InitialLoginStep({
|
||||
> Login </Button>
|
||||
</div>
|
||||
<div className='lg:w-1/6 w-1/4 min-w-[20rem] flex flex-row items-center mt-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>
|
||||
<div className='w-1/2 border-t border-mineshaft-500'/>
|
||||
<div className='w-1/2 border-t border-mineshaft-500' />
|
||||
</div>
|
||||
<div className='lg:w-1/6 w-1/4 min-w-[20rem] text-center rounded-md mt-4'>
|
||||
<Button
|
||||
colorSchema="primary"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
onClick={() => router.push("/saml-sso")}
|
||||
onClick={() => router.push("/saml-sso")}
|
||||
isFullWidth
|
||||
className="h-14 w-full mx-0"
|
||||
>
|
||||
>
|
||||
Continue with SAML SSO
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
124
frontend/src/components/utilities/attemptCliLogin.ts
Normal file
124
frontend/src/components/utilities/attemptCliLogin.ts
Normal file
@@ -0,0 +1,124 @@
|
||||
/* eslint-disable prefer-destructuring */
|
||||
import jsrp from 'jsrp';
|
||||
|
||||
import login1 from '@app/pages/api/auth/Login1';
|
||||
import login2 from '@app/pages/api/auth/Login2';
|
||||
import getOrganizations from '@app/pages/api/organization/getOrgs';
|
||||
import getOrganizationUserProjects from '@app/pages/api/organization/GetOrgUserProjects';
|
||||
import KeyService from '@app/services/KeyService';
|
||||
|
||||
import Telemetry from './telemetry/Telemetry';
|
||||
import { saveTokenToLocalStorage } from './saveTokenToLocalStorage';
|
||||
import SecurityClient from './SecurityClient';
|
||||
|
||||
// eslint-disable-next-line new-cap
|
||||
const client = new jsrp.client();
|
||||
|
||||
interface IsCliLoginSuccessful {
|
||||
loginResponse: {
|
||||
loginOneResponse: {
|
||||
serverPublicKey: string;
|
||||
salt: string;
|
||||
};
|
||||
loginTwoResponse: {
|
||||
mfaEnabled: boolean;
|
||||
token: string;
|
||||
encryptionVersion?: number;
|
||||
protectedKey?: string;
|
||||
protectedKeyIV?: string;
|
||||
protectedKeyTag?: string;
|
||||
publicKey?: string;
|
||||
encryptedPrivateKey?: string;
|
||||
iv?: string;
|
||||
tag?: string;
|
||||
};
|
||||
};
|
||||
success: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether or not login is successful for user with email [email]
|
||||
* and password [password]
|
||||
* @param {string} email - email of user to log in
|
||||
* @param {string} password - password of user to log in
|
||||
*/
|
||||
const attemptLogin = async (
|
||||
{
|
||||
email,
|
||||
password,
|
||||
providerAuthToken,
|
||||
}: {
|
||||
email: string;
|
||||
password: string;
|
||||
providerAuthToken?: string;
|
||||
}
|
||||
): Promise<IsCliLoginSuccessful> => {
|
||||
|
||||
const telemetry = new Telemetry().getInstance();
|
||||
return new Promise((resolve, reject) => {
|
||||
client.init(
|
||||
{
|
||||
username: email,
|
||||
password
|
||||
},
|
||||
async () => {
|
||||
try {
|
||||
const clientPublicKey = client.getPublicKey();
|
||||
const { serverPublicKey, salt } = await login1({
|
||||
email,
|
||||
clientPublicKey,
|
||||
providerAuthToken,
|
||||
});
|
||||
|
||||
client.setSalt(salt);
|
||||
client.setServerPublicKey(serverPublicKey);
|
||||
const clientProof = client.getProof(); // called M1
|
||||
|
||||
const {
|
||||
mfaEnabled,
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
} = await login2(
|
||||
{
|
||||
email,
|
||||
clientProof,
|
||||
providerAuthToken,
|
||||
}
|
||||
);
|
||||
|
||||
resolve({
|
||||
loginResponse: {
|
||||
loginOneResponse: { serverPublicKey, salt },
|
||||
loginTwoResponse: {
|
||||
mfaEnabled,
|
||||
encryptionVersion,
|
||||
protectedKey,
|
||||
protectedKeyIV,
|
||||
protectedKeyTag,
|
||||
token,
|
||||
publicKey,
|
||||
encryptedPrivateKey,
|
||||
iv,
|
||||
tag
|
||||
}
|
||||
},
|
||||
success: true
|
||||
})
|
||||
|
||||
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export default attemptLogin;
|
||||
26
frontend/src/pages/cli-redirect.tsx
Normal file
26
frontend/src/pages/cli-redirect.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import Head from 'next/head';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
|
||||
export default function CliRedirect() {
|
||||
return (
|
||||
<div className='bg-bunker-800 md:h-screen flex flex-col justify-between'>
|
||||
<Head>
|
||||
<title>Infisical Cli | Login Successful!</title>
|
||||
<link rel='icon' href='/infisical.ico' />
|
||||
</Head>
|
||||
<div className='flex flex-col items-center justify-center text-gray-200 h-screen w-screen'>
|
||||
<p className='text-4xl mt-32'>Head back to your terminal!</p>
|
||||
<p className='mt-2 mb-1 text-lg'>
|
||||
You've successfully logged into infisical-cli
|
||||
</p>
|
||||
<Image
|
||||
src='/images/dragon-404.svg'
|
||||
height={554}
|
||||
width={942}
|
||||
alt='infisical dragon - page not found'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user