Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions packages/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
operationCallLogin1V3 = "CallLogin1V3"
operationCallVerifyMfaToken = "CallVerifyMfaToken"
operationCallLogin2V3 = "CallLogin2V3"
operationCallLoginV3 = "CallLoginV3"
operationCallGetAllOrganizations = "CallGetAllOrganizations"
operationCallSelectOrganization = "CallSelectOrganization"
operationCallGetAllWorkSpacesUserBelongsTo = "CallGetAllWorkSpacesUserBelongsTo"
Expand Down Expand Up @@ -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
Expand Down
9 changes: 9 additions & 0 deletions packages/api/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down
135 changes: 38 additions & 97 deletions packages/cmd/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 := &params{
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
}

Expand Down Expand Up @@ -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 {
Expand Down