diff --git a/backend/src/helpers/rateLimiter.ts b/backend/src/helpers/rateLimiter.ts
index 853e7c2ed7..e639f94d11 100644
--- a/backend/src/helpers/rateLimiter.ts
+++ b/backend/src/helpers/rateLimiter.ts
@@ -10,7 +10,7 @@ export const apiLimiter = rateLimit({
// errorHandler: console.error.bind(null, 'rate-limit-mongo')
// }),
windowMs: 60 * 1000,
- max: 240,
+ max: 350,
standardHeaders: true,
legacyHeaders: false,
skip: (request) => {
@@ -30,7 +30,7 @@ const authLimit = rateLimit({
// collectionName: "expressRateRecords-authLimit",
// }),
windowMs: 60 * 1000,
- max: 10,
+ max: 100,
standardHeaders: true,
legacyHeaders: false,
keyGenerator: (req, res) => {
diff --git a/cli/go.mod b/cli/go.mod
index 640e6dc0ca..ce4cf6808e 100644
--- a/cli/go.mod
+++ b/cli/go.mod
@@ -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,12 +15,13 @@ 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
github.com/stretchr/testify v1.8.0
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
- golang.org/x/term v0.5.0
+ golang.org/x/term v0.9.0
)
require (
@@ -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
@@ -58,7 +60,7 @@ require (
go.mongodb.org/mongo-driver v1.10.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
- golang.org/x/sys v0.5.0 // indirect
+ golang.org/x/sys v0.9.0 // indirect
golang.org/x/text v0.7.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
diff --git a/cli/go.sum b/cli/go.sum
index 574452d459..ea16f2d37c 100644
--- a/cli/go.sum
+++ b/cli/go.sum
@@ -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,15 +524,20 @@ 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=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
+golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
+golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go
index 1329b1d2a0..c629130a44 100644
--- a/cli/packages/cmd/login.go
+++ b/cli/packages/cmd/login.go
@@ -6,10 +6,15 @@ package cmd
import (
"encoding/base64"
"encoding/hex"
+ "encoding/json"
+ "os"
"strings"
+ "time"
"errors"
"fmt"
+ "net"
+ "net/http"
"net/url"
"regexp"
@@ -22,10 +27,13 @@ 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"
+ "golang.org/x/term"
)
type params struct {
@@ -39,6 +47,7 @@ type params struct {
const ADD_USER = "Add a new account login"
const REPLACE_USER = "Override current logged in user"
const EXIT_USER_MENU = "Exit"
+const QUIT_BROWSER_LOGIN = "q"
// loginCmd represents the login command
var loginCmd = &cobra.Command{
@@ -89,177 +98,35 @@ var loginCmd = &cobra.Command{
util.HandleError(err, "Unable to parse domain url")
}
}
+ var userCredentialsToBeStored models.UserCredentials
- email, password, err := askForLoginCredentials()
- if err != nil {
- util.HandleError(err, "Unable to parse email and password for authentication")
+ interactiveLogin := false
+ if cmd.Flags().Changed("interactive") {
+ interactiveLogin = true
+ cliDefaultLogin(&userCredentialsToBeStored)
}
- loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password)
- if err != nil {
- log.Warn().Msg("Unable to authenticate with the provided credentials, please ensure your email and password are correct")
- log.Debug().Err(err)
- return
- }
-
- if loginTwoResponse.MfaEnabled {
- i := 1
- for i < 6 {
- mfaVerifyCode := askForMFACode()
-
- httpClient := resty.New()
- httpClient.SetAuthToken(loginTwoResponse.Token)
- verifyMFAresponse, mfaErrorResponse, requestError := api.CallVerifyMfaToken(httpClient, api.VerifyMfaTokenRequest{
- Email: email,
- MFAToken: mfaVerifyCode,
- })
-
- if requestError != nil {
- util.HandleError(err)
- break
- } else if mfaErrorResponse != nil {
- if mfaErrorResponse.Context.Code == "mfa_invalid" {
- msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i)
- fmt.Println(msg)
- if i == 5 {
- util.PrintErrorMessageAndExit("No tries left, please try again in a bit")
- break
- }
- }
-
- if mfaErrorResponse.Context.Code == "mfa_expired" {
- util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again")
- break
- }
- i++
- } else {
- loginTwoResponse.EncryptedPrivateKey = verifyMFAresponse.EncryptedPrivateKey
- loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion
- loginTwoResponse.Iv = verifyMFAresponse.Iv
- loginTwoResponse.ProtectedKey = verifyMFAresponse.ProtectedKey
- loginTwoResponse.ProtectedKeyIV = verifyMFAresponse.ProtectedKeyIV
- loginTwoResponse.ProtectedKeyTag = verifyMFAresponse.ProtectedKeyTag
- loginTwoResponse.PublicKey = verifyMFAresponse.PublicKey
- loginTwoResponse.Tag = verifyMFAresponse.Tag
- loginTwoResponse.Token = verifyMFAresponse.Token
- loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion
- loginTwoResponse.RefreshToken = verifyMFAresponse.RefreshToken
- break
- }
+ //call browser login function
+ if !interactiveLogin {
+ fmt.Printf("\nLogging in via browser... Hit '%s' to cancel\n", QUIT_BROWSER_LOGIN)
+ userCredentialsToBeStored, err = browserCliLogin()
+ if err != nil {
+ //default to cli login on error
+ cliDefaultLogin(&userCredentialsToBeStored)
}
}
- var decryptedPrivateKey []byte
-
- if loginTwoResponse.EncryptionVersion == 1 {
- log.Debug().Msg("Login version 1")
- encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
- tag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
- if err != nil {
- util.HandleError(err)
- }
-
- IV, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
- if err != nil {
- util.HandleError(err)
- }
-
- paddedPassword := fmt.Sprintf("%032s", password)
- key := []byte(paddedPassword)
-
- computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
- if err != nil || len(computedDecryptedPrivateKey) == 0 {
- util.HandleError(err)
- }
-
- decryptedPrivateKey = computedDecryptedPrivateKey
-
- } else if loginTwoResponse.EncryptionVersion == 2 {
- log.Debug().Msg("Login version 2")
- protectedKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKey)
- if err != nil {
- util.HandleError(err)
- }
-
- protectedKeyTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyTag)
- if err != nil {
- util.HandleError(err)
- }
-
- protectedKeyIV, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyIV)
- if err != nil {
- util.HandleError(err)
- }
-
- nonProtectedTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
- if err != nil {
- util.HandleError(err)
- }
-
- nonProtectedIv, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
- if err != nil {
- util.HandleError(err)
- }
-
- parameters := ¶ms{
- memory: 64 * 1024,
- iterations: 3,
- parallelism: 1,
- keyLength: 32,
- }
-
- derivedKey, err := generateFromPassword(password, []byte(loginOneResponse.Salt), parameters)
- if err != nil {
- util.HandleError(fmt.Errorf("unable to generate argon hash from password [err=%s]", err))
- }
-
- decryptedProtectedKey, err := crypto.DecryptSymmetric(derivedKey, protectedKey, protectedKeyTag, protectedKeyIV)
- if err != nil {
- util.HandleError(fmt.Errorf("unable to get decrypted protected key [err=%s]", err))
- }
-
- encryptedPrivateKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
- if err != nil {
- util.HandleError(err)
- }
-
- decryptedProtectedKeyInHex, err := hex.DecodeString(string(decryptedProtectedKey))
- if err != nil {
- util.HandleError(err)
- }
-
- computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv)
- if err != nil {
- util.HandleError(err)
- }
-
- decryptedPrivateKey = computedDecryptedPrivateKey
- } else {
- util.PrintErrorMessageAndExit("Insufficient details to decrypt private key")
- }
-
- if string(decryptedPrivateKey) == "" || email == "" || loginTwoResponse.Token == "" {
- log.Debug().Msgf("[decryptedPrivateKey=%s] [email=%s] [loginTwoResponse.Token=%s]", string(decryptedPrivateKey), email, loginTwoResponse.Token)
- util.PrintErrorMessageAndExit("We were unable to fetch required details to complete your login. Run with -d to see more info")
- }
-
- userCredentialsToBeStored := &models.UserCredentials{
- Email: email,
- PrivateKey: string(decryptedPrivateKey),
- JTWToken: loginTwoResponse.Token,
- RefreshToken: loginTwoResponse.RefreshToken,
- }
-
- err = util.StoreUserCredsInKeyRing(userCredentialsToBeStored)
+ err = util.StoreUserCredsInKeyRing(&userCredentialsToBeStored)
if err != nil {
currentVault, _ := util.GetCurrentVaultBackend()
log.Error().Msgf("Unable to store your credentials in system vault [%s]. Rerun with flag -d to see full logs", currentVault)
log.Error().Msgf("\nTo trouble shoot further, read https://infisical.com/docs/cli/faq")
log.Debug().Err(err)
- return
+ //return here
+ util.HandleError(err)
}
- err = util.WriteInitalConfig(userCredentialsToBeStored)
+ err = util.WriteInitalConfig(&userCredentialsToBeStored)
if err != nil {
util.HandleError(err, "Unable to write write to Infisical Config file. Please try again")
}
@@ -269,8 +136,9 @@ var loginCmd = &cobra.Command{
whilte := color.New(color.FgGreen)
boldWhite := whilte.Add(color.Bold)
+ time.Sleep(time.Second * 1)
boldWhite.Printf(">>>> Welcome to Infisical!")
- boldWhite.Printf(" You are now logged in as %v <<<< \n", email)
+ boldWhite.Printf(" You are now logged in as %v <<<< \n", userCredentialsToBeStored.Email)
plainBold := color.New(color.Bold)
@@ -281,8 +149,170 @@ var loginCmd = &cobra.Command{
},
}
+func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) {
+ 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 here
+ util.HandleError(err)
+ }
+
+ if loginTwoResponse.MfaEnabled {
+ i := 1
+ for i < 6 {
+ mfaVerifyCode := askForMFACode()
+
+ httpClient := resty.New()
+ httpClient.SetAuthToken(loginTwoResponse.Token)
+ verifyMFAresponse, mfaErrorResponse, requestError := api.CallVerifyMfaToken(httpClient, api.VerifyMfaTokenRequest{
+ Email: email,
+ MFAToken: mfaVerifyCode,
+ })
+
+ if requestError != nil {
+ util.HandleError(err)
+ break
+ } else if mfaErrorResponse != nil {
+ if mfaErrorResponse.Context.Code == "mfa_invalid" {
+ msg := fmt.Sprintf("Incorrect, verification code. You have %v attempts left", 5-i)
+ fmt.Println(msg)
+ if i == 5 {
+ util.PrintErrorMessageAndExit("No tries left, please try again in a bit")
+ break
+ }
+ }
+
+ if mfaErrorResponse.Context.Code == "mfa_expired" {
+ util.PrintErrorMessageAndExit("Your 2FA verification code has expired, please try logging in again")
+ break
+ }
+ i++
+ } else {
+ loginTwoResponse.EncryptedPrivateKey = verifyMFAresponse.EncryptedPrivateKey
+ loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion
+ loginTwoResponse.Iv = verifyMFAresponse.Iv
+ loginTwoResponse.ProtectedKey = verifyMFAresponse.ProtectedKey
+ loginTwoResponse.ProtectedKeyIV = verifyMFAresponse.ProtectedKeyIV
+ loginTwoResponse.ProtectedKeyTag = verifyMFAresponse.ProtectedKeyTag
+ loginTwoResponse.PublicKey = verifyMFAresponse.PublicKey
+ loginTwoResponse.Tag = verifyMFAresponse.Tag
+ loginTwoResponse.Token = verifyMFAresponse.Token
+ loginTwoResponse.EncryptionVersion = verifyMFAresponse.EncryptionVersion
+
+ break
+ }
+ }
+ }
+
+ var decryptedPrivateKey []byte
+
+ if loginTwoResponse.EncryptionVersion == 1 {
+ log.Debug().Msg("Login version 1")
+ encryptedPrivateKey, _ := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
+ tag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ IV, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ paddedPassword := fmt.Sprintf("%032s", password)
+ key := []byte(paddedPassword)
+
+ computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(key, encryptedPrivateKey, tag, IV)
+ if err != nil || len(computedDecryptedPrivateKey) == 0 {
+ util.HandleError(err)
+ }
+
+ decryptedPrivateKey = computedDecryptedPrivateKey
+
+ } else if loginTwoResponse.EncryptionVersion == 2 {
+ log.Debug().Msg("Login version 2")
+ protectedKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKey)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ protectedKeyTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyTag)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ protectedKeyIV, err := base64.StdEncoding.DecodeString(loginTwoResponse.ProtectedKeyIV)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ nonProtectedTag, err := base64.StdEncoding.DecodeString(loginTwoResponse.Tag)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ nonProtectedIv, err := base64.StdEncoding.DecodeString(loginTwoResponse.Iv)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ parameters := ¶ms{
+ memory: 64 * 1024,
+ iterations: 3,
+ parallelism: 1,
+ keyLength: 32,
+ }
+
+ derivedKey, err := generateFromPassword(password, []byte(loginOneResponse.Salt), parameters)
+ if err != nil {
+ util.HandleError(fmt.Errorf("unable to generate argon hash from password [err=%s]", err))
+ }
+
+ decryptedProtectedKey, err := crypto.DecryptSymmetric(derivedKey, protectedKey, protectedKeyTag, protectedKeyIV)
+ if err != nil {
+ util.HandleError(fmt.Errorf("unable to get decrypted protected key [err=%s]", err))
+ }
+
+ encryptedPrivateKey, err := base64.StdEncoding.DecodeString(loginTwoResponse.EncryptedPrivateKey)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ decryptedProtectedKeyInHex, err := hex.DecodeString(string(decryptedProtectedKey))
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ computedDecryptedPrivateKey, err := crypto.DecryptSymmetric(decryptedProtectedKeyInHex, encryptedPrivateKey, nonProtectedTag, nonProtectedIv)
+ if err != nil {
+ util.HandleError(err)
+ }
+
+ decryptedPrivateKey = computedDecryptedPrivateKey
+ } else {
+ util.PrintErrorMessageAndExit("Insufficient details to decrypt private key")
+ }
+
+ if string(decryptedPrivateKey) == "" || email == "" || loginTwoResponse.Token == "" {
+ log.Debug().Msgf("[decryptedPrivateKey=%s] [email=%s] [loginTwoResponse.Token=%s]", string(decryptedPrivateKey), email, loginTwoResponse.Token)
+ util.PrintErrorMessageAndExit("We were unable to fetch required details to complete your login. Run with -d to see more info")
+ }
+
+ //updating usercredentials
+ userCredentialsToBeStored.Email = email
+ userCredentialsToBeStored.PrivateKey = string(decryptedPrivateKey)
+ userCredentialsToBeStored.JTWToken = loginTwoResponse.Token
+}
+
func init() {
rootCmd.AddCommand(loginCmd)
+ loginCmd.Flags().BoolP("interactive", "i", false, "login via the command line")
}
func DomainOverridePrompt() (bool, error) {
@@ -327,7 +357,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
}
@@ -342,7 +373,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()
@@ -350,8 +381,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
}
@@ -365,6 +397,7 @@ func askForLoginCredentials() (email string, password string, err error) {
return nil
}
+ fmt.Println("Enter Credentials...")
emailPrompt := promptui.Prompt{
Label: "Email",
Validate: validateEmail,
@@ -477,3 +510,121 @@ func askForMFACode() string {
return mfaVerifyCode
}
+
+// Manages the browser login flow.
+// Returns a UserCredentials object on success and an error on failure
+func browserCliLogin() (models.UserCredentials, error) {
+ SERVER_TIMEOUT := 60 * 10
+
+ //create listener
+ listener, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ return models.UserCredentials{}, 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 models.UserCredentials{}, err
+ }
+
+ //flow channels
+ success := make(chan models.UserCredentials)
+ failure := make(chan error)
+ timeout := time.After(time.Second * time.Duration(SERVER_TIMEOUT))
+ quit := make(chan bool)
+
+ //terminal state
+ var oldState term.State
+
+ //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 quitBrowserLogin(quit, &oldState)
+ go http.Serve(listener, corsHandler)
+
+ for {
+ select {
+ case loginResponse := <-success:
+ err = closeListener(&listener)
+ restoreTerminal(&oldState)
+ return loginResponse, nil
+
+ case err = <-failure:
+ err = closeListener(&listener)
+ restoreTerminal(&oldState)
+ return models.UserCredentials{}, err
+
+ case _ = <-timeout:
+ err = closeListener(&listener)
+ restoreTerminal(&oldState)
+ return models.UserCredentials{}, errors.New("server timeout")
+
+ case _ = <-quit:
+ return models.UserCredentials{}, errors.New("quitting browser login, defaulting to cli...")
+
+ }
+ }
+}
+
+func restoreTerminal(oldState *term.State) {
+ term.Restore(int(os.Stdin.Fd()), oldState)
+}
+
+// listens to 'q' input on terminal and
+// sends 'true' to 'quit' channel
+func quitBrowserLogin(quit chan bool, oState *term.State) {
+ //
+ oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
+ if err != nil {
+ return
+ }
+ *oState = *oldState
+ defer restoreTerminal(oldState)
+ b := make([]byte, 1)
+ for {
+ _, _ = os.Stdin.Read(b)
+ if string(b) == QUIT_BROWSER_LOGIN {
+ quit <- true
+ break
+ }
+ }
+}
+
+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 models.UserCredentials, failure chan error) http.HandlerFunc {
+
+ return func(w http.ResponseWriter, r *http.Request) {
+ var loginResponse models.UserCredentials
+
+ decoder := json.NewDecoder(r.Body)
+ err := decoder.Decode(&loginResponse)
+ if err != nil {
+ failure <- err
+ }
+
+ w.WriteHeader(http.StatusOK)
+ success <- loginResponse
+
+ }
+}
diff --git a/cli/packages/config/config.go b/cli/packages/config/config.go
index e27bc8f683..c5e162c922 100644
--- a/cli/packages/config/config.go
+++ b/cli/packages/config/config.go
@@ -2,3 +2,4 @@ package config
var INFISICAL_URL string
var INFISICAL_URL_MANUAL_OVERRIDE string
+var INFISICAL_LOGIN_URL string
diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go
index 5c169f6d50..ee2532ee8e 100644
--- a/cli/packages/util/constants.go
+++ b/cli/packages/util/constants.go
@@ -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"
diff --git a/docs/integrations/platforms/kubernetes.mdx b/docs/integrations/platforms/kubernetes.mdx
index efe5297ad9..58a8363057 100644
--- a/docs/integrations/platforms/kubernetes.mdx
+++ b/docs/integrations/platforms/kubernetes.mdx
@@ -51,16 +51,10 @@ spec:
# The host that should be used to pull secrets from. If left empty, the value specified in Global configuration will be used
hostAPI: https://app.infisical.com/api
authentication:
- serviceToken: # <-- option 1
+ serviceToken:
serviceTokenSecretReference:
secretName: service-token
secretNamespace: option
- serviceAccount: # <-- method 2
- serviceAccountSecretReference:
- secretName: service-account
- secretNamespace: default
- projectId: "6439ec224cfbf7ea2a95b651"
- environmentName: "dev"
managedSecretReference:
secretName: managed-secret # <-- the name of kubernetes secret that will be created
secretNamespace: default # <-- where the kubernetes secret that will be created
@@ -86,7 +80,7 @@ spec:
Head back to your terminal!
++ You've successfully logged into infisical-cli +
+