diff --git a/packages/api/api.go b/packages/api/api.go index 15f75a57..a9b204b6 100644 --- a/packages/api/api.go +++ b/packages/api/api.go @@ -19,6 +19,7 @@ const ( operationCallLogin1V3 = "CallLogin1V3" operationCallVerifyMfaToken = "CallVerifyMfaToken" operationCallLogin2V3 = "CallLogin2V3" + operationCallLoginV3 = "CallLoginV3" operationCallGetAllOrganizations = "CallGetAllOrganizations" operationCallSelectOrganization = "CallSelectOrganization" operationCallGetAllWorkSpacesUserBelongsTo = "CallGetAllWorkSpacesUserBelongsTo" @@ -100,6 +101,26 @@ func CallLogin1V2(httpClient *resty.Client, request GetLoginOneV2Request) (GetLo return loginOneV2Response, nil } +func CallLoginV3(httpClient *resty.Client, request GetLoginV3Request) (GetLoginV3Response, error) { + var loginV3Response GetLoginV3Response + response, err := httpClient. + R(). + SetResult(&loginV3Response). + SetHeader("User-Agent", USER_AGENT). + SetBody(request). + Post(fmt.Sprintf("%v/v3/auth/login", config.INFISICAL_URL)) + + if err != nil { + return GetLoginV3Response{}, NewGenericRequestError(operationCallLoginV3, err) + } + + if response.IsError() { + return GetLoginV3Response{}, NewAPIErrorWithResponse(operationCallLoginV3, response, nil) + } + + return loginV3Response, nil +} + func CallVerifyMfaToken(httpClient *resty.Client, request VerifyMfaTokenRequest) (*VerifyMfaTokenResponse, *VerifyMfaTokenErrorResponse, error) { var verifyMfaTokenResponse VerifyMfaTokenResponse var responseError VerifyMfaTokenErrorResponse diff --git a/packages/api/model.go b/packages/api/model.go index 12bf2429..3f10b4ca 100644 --- a/packages/api/model.go +++ b/packages/api/model.go @@ -585,6 +585,15 @@ type CreateDynamicSecretLeaseV1Response struct { Data map[string]interface{} `json:"data"` } +type GetLoginV3Request struct { + Email string `json:"email"` + Password string `json:"password"` +} + +type GetLoginV3Response struct { + AccessToken string `json:"accessToken"` +} + type GetRawSecretsV3Request struct { Environment string `json:"environment"` WorkspaceId string `json:"workspaceId"` diff --git a/packages/cmd/login.go b/packages/cmd/login.go index fd3ce156..ea4c68bf 100644 --- a/packages/cmd/login.go +++ b/packages/cmd/login.go @@ -25,7 +25,6 @@ import ( "github.com/Infisical/infisical-merge/packages/api" "github.com/Infisical/infisical-merge/packages/config" - "github.com/Infisical/infisical-merge/packages/crypto" "github.com/Infisical/infisical-merge/packages/models" "github.com/Infisical/infisical-merge/packages/srp" "github.com/Infisical/infisical-merge/packages/util" @@ -280,7 +279,22 @@ func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) { util.HandleError(err, "Unable to parse email and password for authentication") } - loginOneResponse, loginTwoResponse, err := getFreshUserCredentials(email, password) + loginV3Response, err := getFreshUserCredentials(email, password) + if err == nil { + userCredentialsToBeStored.Email = email + userCredentialsToBeStored.PrivateKey = "" + userCredentialsToBeStored.JTWToken = loginV3Response.AccessToken + return + } + + // TODO(daniel): At a later time we should re-add this check, but we don't want to break older Infisical instances that doesn't have the latest SRP removal initiative on them. + // if !strings.Contains(err.Error(), "LegacyEncryptionScheme") { + // util.HandleError(err) + // } + + log.Info().Msg("Unable to authenticate with the provided credentials, falling back to SRP authentication") + + _, loginTwoResponse, err := getFreshUserCredentialsWithSrp(email, password) if err != nil { fmt.Println("Unable to authenticate with the provided credentials, please try again") log.Debug().Err(err) @@ -338,105 +352,12 @@ func cliDefaultLogin(userCredentialsToBeStored *models.UserCredentials) { } } - 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") - } // Login is successful so ask user to choose organization newJwtToken := GetJwtTokenWithOrganizationId(loginTwoResponse.Token, email) //updating usercredentials userCredentialsToBeStored.Email = email - userCredentialsToBeStored.PrivateKey = string(decryptedPrivateKey) + userCredentialsToBeStored.PrivateKey = "" userCredentialsToBeStored.JTWToken = newJwtToken } @@ -665,7 +586,27 @@ func askForLoginCredentials() (email string, password string, err error) { return userEmail, userPassword, nil } -func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2Response, *api.GetLoginTwoV2Response, error) { +func getFreshUserCredentials(email string, password string) (*api.GetLoginV3Response, error) { + log.Debug().Msg(fmt.Sprint("getFreshUserCredentials: ", "email", email, "password: ", password)) + httpClient, err := util.GetRestyClientWithCustomHeaders() + if err != nil { + return nil, err + } + httpClient.SetRetryCount(5) + + loginV3Response, err := api.CallLoginV3(httpClient, api.GetLoginV3Request{ + Email: email, + Password: password, + }) + + if err != nil { + return nil, err + } + + return &loginV3Response, nil +} + +func getFreshUserCredentialsWithSrp(email string, password string) (*api.GetLoginOneV2Response, *api.GetLoginTwoV2Response, error) { log.Debug().Msg(fmt.Sprint("getFreshUserCredentials: ", "email", email, "password: ", password)) httpClient, err := util.GetRestyClientWithCustomHeaders() if err != nil {