diff --git a/backend/src/server/plugins/error-handler.ts b/backend/src/server/plugins/error-handler.ts index 3320c7d87..871b071e7 100644 --- a/backend/src/server/plugins/error-handler.ts +++ b/backend/src/server/plugins/error-handler.ts @@ -1,5 +1,6 @@ import { ForbiddenError } from "@casl/ability"; import fastifyPlugin from "fastify-plugin"; +import { JsonWebTokenError } from "jsonwebtoken"; import { ZodError } from "zod"; import { @@ -11,6 +12,12 @@ import { UnauthorizedError } from "@app/lib/errors"; +enum JWTErrors { + JwtExpired = "jwt expired", + JwtMalformed = "jwt malformed", + InvalidAlgorithm = "invalid algorithm" +} + export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider) => { server.setErrorHandler((error, req, res) => { req.log.error(error); @@ -36,6 +43,27 @@ export const fastifyErrHandler = fastifyPlugin(async (server: FastifyZodProvider status: error.status, detail: error.detail }); + // Handle JWT errors and make them more human-readable for the end-user. + } else if (error instanceof JsonWebTokenError) { + const message = (() => { + if (error.message === JWTErrors.JwtExpired) { + return "Your token has expired. Please re-authenticate."; + } + if (error.message === JWTErrors.JwtMalformed) { + return "The provided access token is malformed. Please use a valid token or generate a new one and try again."; + } + if (error.message === JWTErrors.InvalidAlgorithm) { + return "The access token is signed with an invalid algorithm. Please provide a valid token and try again."; + } + + return error.message; + })(); + + void res.status(401).send({ + statusCode: 401, + error: "TokenError", + message + }); } else { void res.send(error); } diff --git a/backend/src/services/project-bot/project-bot-fns.ts b/backend/src/services/project-bot/project-bot-fns.ts index 9cdb52cff..4efcbb34b 100644 --- a/backend/src/services/project-bot/project-bot-fns.ts +++ b/backend/src/services/project-bot/project-bot-fns.ts @@ -26,7 +26,10 @@ export const getBotKeyFnFactory = ( ) => { const getBotKeyFn = async (projectId: string) => { const project = await projectDAL.findById(projectId); - if (!project) throw new BadRequestError({ message: "Project not found during bot lookup." }); + if (!project) + throw new BadRequestError({ + message: "Project not found during bot lookup. Are you sure you are using the correct project ID?" + }); if (project.version === 3) { return { project, shouldUseSecretV2Bridge: true }; diff --git a/backend/src/services/secret-import/secret-import-service.ts b/backend/src/services/secret-import/secret-import-service.ts index c3d1a6791..45cecf03d 100644 --- a/backend/src/services/secret-import/secret-import-service.ts +++ b/backend/src/services/secret-import/secret-import-service.ts @@ -512,7 +512,11 @@ export const secretImportServiceFactory = ({ return importedSecrets; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const importedSecrets = await fnSecretsFromImports({ allowedImports, folderDAL, secretDAL, secretImportDAL }); return importedSecrets.map((el) => ({ diff --git a/backend/src/services/secret/secret-fns.ts b/backend/src/services/secret/secret-fns.ts index 7837b716b..e77972d37 100644 --- a/backend/src/services/secret/secret-fns.ts +++ b/backend/src/services/secret/secret-fns.ts @@ -832,7 +832,11 @@ export const createManySecretsRawFnFactory = ({ secretDAL }); - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const inputSecrets = secrets.map((secret) => { const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretName, botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretValue || "", botKey); @@ -993,7 +997,11 @@ export const updateManySecretsRawFnFactory = ({ return updatedSecrets; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const blindIndexCfg = await secretBlindIndexDAL.findOne({ projectId }); if (!blindIndexCfg) throw new BadRequestError({ message: "Blind index not found", name: "Update secret" }); diff --git a/backend/src/services/secret/secret-service.ts b/backend/src/services/secret/secret-service.ts index e502f577a..124515c8b 100644 --- a/backend/src/services/secret/secret-service.ts +++ b/backend/src/services/secret/secret-service.ts @@ -985,7 +985,11 @@ export const secretServiceFactory = ({ return { secrets, imports }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const { secrets, imports } = await getSecrets({ actorId, @@ -1146,7 +1150,10 @@ export const secretServiceFactory = ({ }); if (!botKey) - throw new BadRequestError({ message: "Please upgrade your project first", name: "bot_not_found_error" }); + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const decryptedSecret = decryptSecretRaw(encryptedSecret, botKey); if (expandSecretReferences) { @@ -1238,7 +1245,11 @@ export const secretServiceFactory = ({ return { secret, type: SecretProtectionType.Direct as const }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretName, botKey); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey); @@ -1376,7 +1387,11 @@ export const secretServiceFactory = ({ return { type: SecretProtectionType.Direct as const, secret }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey); const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey); @@ -1498,7 +1513,11 @@ export const secretServiceFactory = ({ }); return { type: SecretProtectionType.Direct as const, secret }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); if (policy) { const approval = await secretApprovalRequestService.generateSecretApprovalRequest({ policy, @@ -1598,7 +1617,11 @@ export const secretServiceFactory = ({ return { secrets, type: SecretProtectionType.Direct as const }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const sanitizedSecrets = inputSecrets.map( ({ secretComment, secretKey, metadata, tagIds, secretValue, skipMultilineEncoding }) => { const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretKey, botKey); @@ -1720,7 +1743,11 @@ export const secretServiceFactory = ({ return { type: SecretProtectionType.Direct as const, secrets }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); const sanitizedSecrets = inputSecrets.map( ({ secretComment, @@ -1848,7 +1875,11 @@ export const secretServiceFactory = ({ return { type: SecretProtectionType.Direct as const, secrets }; } - if (!botKey) throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + if (!botKey) + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); if (policy) { const approval = await secretApprovalRequestService.generateSecretApprovalRequest({ @@ -2182,7 +2213,10 @@ export const secretServiceFactory = ({ } if (!botKey) - throw new BadRequestError({ message: "Please upgrade your project first", name: "bot_not_found_error" }); + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); await secretDAL.transaction(async (tx) => { const secrets = await secretDAL.findAllProjectSecretValues(projectId, tx); @@ -2265,7 +2299,10 @@ export const secretServiceFactory = ({ const { botKey } = await projectBotService.getBotKey(project.id); if (!botKey) { - throw new BadRequestError({ message: "Project bot not found", name: "bot_not_found_error" }); + throw new BadRequestError({ + message: "Project bot not found. Please upgrade your project.", + name: "bot_not_found_error" + }); } const sourceFolder = await folderDAL.findBySecretPath(project.id, sourceEnvironment, sourceSecretPath); diff --git a/cli/packages/cmd/root.go b/cli/packages/cmd/root.go index 482c6f78a..e40c07022 100644 --- a/cli/packages/cmd/root.go +++ b/cli/packages/cmd/root.go @@ -4,6 +4,7 @@ Copyright (c) 2023 Infisical Inc. package cmd import ( + "fmt" "os" "strings" @@ -43,14 +44,26 @@ func init() { rootCmd.PersistentFlags().Bool("silent", false, "Disable output of tip/info messages. Useful when running in scripts or CI/CD pipelines.") rootCmd.PersistentPreRun = func(cmd *cobra.Command, args []string) { silent, err := cmd.Flags().GetBool("silent") - config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL) if err != nil { util.HandleError(err) } + config.INFISICAL_URL = util.AppendAPIEndpoint(config.INFISICAL_URL) + if !util.IsRunningInDocker() && !silent { util.CheckForUpdate() } + + loggedInDetails, err := util.GetCurrentLoggedInUserDetails() + + if !silent && err == nil && loggedInDetails.IsUserLoggedIn && !loggedInDetails.LoginExpired { + token, err := util.GetInfisicalToken(cmd) + + if err == nil && token != nil { + util.PrintWarning(fmt.Sprintf("Your logged-in session is being overwritten by the token provided from the %s.", token.Source)) + } + } + } // if config.INFISICAL_URL is set to the default value, check if INFISICAL_URL is set in the environment diff --git a/cli/packages/cmd/secrets.go b/cli/packages/cmd/secrets.go index c9ac36852..e2987cc7d 100644 --- a/cli/packages/cmd/secrets.go +++ b/cli/packages/cmd/secrets.go @@ -160,19 +160,19 @@ var secretsSetCmd = &cobra.Command{ util.HandleError(err, "Unable to parse flag") } - if (token == nil) { + if token == nil { util.RequireLocalWorkspaceFile() } environmentName, _ := cmd.Flags().GetString("env") if !cmd.Flags().Changed("env") { - environmentFromWorkspace := util.GetEnvFromWorkspaceFile() + environmentFromWorkspace := util.GetEnvFromWorkspaceFile() if environmentFromWorkspace != "" { environmentName = environmentFromWorkspace } } - projectId, err := cmd.Flags().GetString("projectId") + projectId, err := cmd.Flags().GetString("projectId") if err != nil { util.HandleError(err, "Unable to parse flag") } diff --git a/cli/packages/models/cli.go b/cli/packages/models/cli.go index 1bad5e327..62ff07190 100644 --- a/cli/packages/models/cli.go +++ b/cli/packages/models/cli.go @@ -63,8 +63,9 @@ type DynamicSecretLease struct { } type TokenDetails struct { - Type string - Token string + Type string + Token string + Source string } type SingleFolder struct { diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go index b758ebd9d..11a1e3e0a 100644 --- a/cli/packages/util/helper.go +++ b/cli/packages/util/helper.go @@ -87,11 +87,15 @@ func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err erro return nil, err } + var source = "--token flag" + 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) 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) } } @@ -101,14 +105,16 @@ func GetInfisicalToken(cmd *cobra.Command) (token *models.TokenDetails, err erro if strings.HasPrefix(infisicalToken, "st.") { return &models.TokenDetails{ - Type: SERVICE_TOKEN_IDENTIFIER, - Token: infisicalToken, + Type: SERVICE_TOKEN_IDENTIFIER, + Token: infisicalToken, + Source: source, }, nil } return &models.TokenDetails{ - Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, - Token: infisicalToken, + Type: UNIVERSAL_AUTH_TOKEN_IDENTIFIER, + Token: infisicalToken, + Source: source, }, nil }