diff --git a/packages/cmd/root.go b/packages/cmd/root.go index 1a8bfa62..9cece465 100644 --- a/packages/cmd/root.go +++ b/packages/cmd/root.go @@ -112,7 +112,7 @@ func init() { loggedInDetails, err := util.GetCurrentLoggedInUserDetails(false) if !silent && err == nil && loggedInDetails.IsUserLoggedIn && !loggedInDetails.LoginExpired { - token, err := util.GetInfisicalToken(cmd) + token, err := util.DetectInfisicalToken(cmd) if err == nil && token != nil { util.PrintWarningWithWriter(fmt.Sprintf("Your logged-in session is being overwritten by the token provided from the %s.", token.Source), cmd.ErrOrStderr()) diff --git a/packages/util/helper.go b/packages/util/helper.go index cdaef77c..9c29f625 100644 --- a/packages/util/helper.go +++ b/packages/util/helper.go @@ -279,59 +279,135 @@ func IsSecretTypeValid(s string) bool { return false } -func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err error) { - infisicalToken, err := cmd.Flags().GetString("token") +// DetectInfisicalToken checks for locally-available tokens and credentials +// without performing any network calls. Use this when you only need to know +// whether a token source is present (e.g. for the "session overwritten" warning +// in PersistentPreRun). +func DetectInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err error) { + infisicalToken, source := lookupLocalToken(cmd) + if infisicalToken != "" { + return classifyToken(infisicalToken, source), nil + } + + clientId := os.Getenv(INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME) + clientSecret := os.Getenv(INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME) + if clientId != "" && clientSecret != "" { + return &models.TokenDetails{ + Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, + Source: fmt.Sprintf("%s and %s environment variables", INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME, INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME), + }, nil + } - if err != nil { - return nil, err + return nil, nil +} + +func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err error) { + infisicalToken, source := lookupLocalToken(cmd) + if infisicalToken != "" { + return classifyToken(infisicalToken, source), nil } - var source = "--token flag" + // Skip auto-login when an explicit auth method is requested via + // --auth-method flag or INFISICAL_AUTH_METHOD env var, so that + // other auth flows (AWS, GCP, K8s, etc.) are not short-circuited. + if isAuthMethodSpecified(cmd) { + return nil, nil + } - if infisicalToken == "" { // If no flag is passed, we first check for the universal auth access token env variable. - infisicalToken = os.Getenv(INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME) - source = fmt.Sprintf("%s environment variable", INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME) + clientId := os.Getenv(INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME) + clientSecret := os.Getenv(INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME) - if infisicalToken == "" { // If it's still empty after the first env check, we check for the service token env variable. - infisicalToken = os.Getenv(INFISICAL_TOKEN_NAME) - source = fmt.Sprintf("%s environment variable", INFISICAL_TOKEN_NAME) + if clientId != "" && clientSecret != "" { + log.Debug().Msg("No explicit token found, attempting automatic authentication via Universal Auth client credentials") + loginResponse, err := UniversalAuthLogin(clientId, clientSecret) + if err != nil { + return nil, fmt.Errorf("failed to authenticate with Universal Auth using %s and %s environment variables: %w", + INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME, INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME, err) } - if infisicalToken == "" { // if its still empty, check for the `TOKEN` environment variable (for gateway helm) - infisicalToken = os.Getenv(INFISICAL_GATEWAY_TOKEN_NAME_LEGACY) - source = fmt.Sprintf("%s environment variable", INFISICAL_GATEWAY_TOKEN_NAME_LEGACY) - } + return &models.TokenDetails{ + Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, + Token: loginResponse.AccessToken, + Source: fmt.Sprintf("%s and %s environment variables", INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME, INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME), + }, nil } - if infisicalToken == "" { // If it's empty, we return nothing at all. - return nil, nil + return nil, nil +} + +// lookupLocalToken checks for an explicit token from flags and env vars. +// No network calls are made. +func lookupLocalToken(cmd *cobra.Command) (token string, source string) { + infisicalToken, err := cmd.Flags().GetString("token") + if err != nil { + return "", "" } + if infisicalToken != "" { + return infisicalToken, "--token flag" + } + + if v := os.Getenv(INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME); v != "" { + return v, fmt.Sprintf("%s environment variable", INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME) + } + + if v := os.Getenv(INFISICAL_TOKEN_NAME); v != "" { + return v, fmt.Sprintf("%s environment variable", INFISICAL_TOKEN_NAME) + } + + if v := os.Getenv(INFISICAL_GATEWAY_TOKEN_NAME_LEGACY); v != "" { + return v, fmt.Sprintf("%s environment variable", INFISICAL_GATEWAY_TOKEN_NAME_LEGACY) + } + + return "", "" +} + +func classifyToken(infisicalToken, source string) *models.TokenDetails { if strings.HasPrefix(infisicalToken, "st.") { return &models.TokenDetails{ Type: SERVICE_TOKEN_IDENTIFIER, Token: infisicalToken, Source: source, - }, nil + } } - return &models.TokenDetails{ Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, Token: infisicalToken, Source: source, - }, nil + } +} +// isAuthMethodSpecified returns true when the caller has explicitly requested +// a machine-identity auth strategy, either via the --auth-method CLI flag or +// the INFISICAL_AUTH_METHOD environment variable. The env var is only checked +// when the command actually defines the --auth-method flag (e.g. gateway, relay), +// so that commands like run/export/secrets are not affected by a globally-set +// INFISICAL_AUTH_METHOD. +func isAuthMethodSpecified(cmd *cobra.Command) bool { + f := cmd.Flags().Lookup("auth-method") + if f == nil { + return false + } + if f.Changed { + return true + } + if os.Getenv(INFISICAL_AUTH_METHOD_NAME) != "" { + return true + } + return false } +const universalAuthAutoLoginRetries = 3 + func UniversalAuthLogin(clientId string, clientSecret string) (api.UniversalAuthLoginResponse, error) { httpClient, err := GetRestyClientWithCustomHeaders() if err != nil { return api.UniversalAuthLoginResponse{}, err } - httpClient.SetRetryCount(10000). - SetRetryMaxWaitTime(20 * time.Second). - SetRetryWaitTime(5 * time.Second) + httpClient.SetRetryCount(universalAuthAutoLoginRetries). + SetRetryMaxWaitTime(5 * time.Second). + SetRetryWaitTime(2 * time.Second) tokenResponse, err := api.CallUniversalAuthLogin(httpClient, api.UniversalAuthLoginRequest{ClientId: clientId, ClientSecret: clientSecret}) if err != nil {