diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/constants/google-permanent-oauth-error-codes.constant.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/constants/google-permanent-oauth-error-codes.constant.ts new file mode 100644 index 0000000000..bf1b3e7b81 --- /dev/null +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/constants/google-permanent-oauth-error-codes.constant.ts @@ -0,0 +1,11 @@ +/** + * @see https://developers.google.com/identity/protocols/oauth2/web-server#authorization-errors + */ +export const GOOGLE_PERMANENT_OAUTH_ERROR_CODES = new Set([ + 'invalid_grant', + 'invalid_client', + 'unauthorized_client', + 'unsupported_grant_type', + 'invalid_scope', + 'admin_policy_enforced', +]); diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/utils/parse-google-oauth-error.util.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/utils/parse-google-oauth-error.util.ts index 5ac15a4f43..4ac8c0e901 100644 --- a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/utils/parse-google-oauth-error.util.ts +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/google/utils/parse-google-oauth-error.util.ts @@ -4,6 +4,7 @@ import { ConnectedAccountRefreshAccessTokenException, ConnectedAccountRefreshAccessTokenExceptionCode, } from 'src/engine/metadata-modules/connected-account/exceptions/connected-account-refresh-tokens.exception'; +import { GOOGLE_PERMANENT_OAUTH_ERROR_CODES } from 'src/modules/connected-account/refresh-tokens-manager/drivers/google/constants/google-permanent-oauth-error-codes.constant'; import { isGmailNetworkError } from 'src/modules/messaging/message-import-manager/drivers/gmail/utils/is-gmail-network-error.util'; export const parseGoogleOAuthError = ( @@ -20,63 +21,22 @@ export const parseGoogleOAuthError = ( const googleOAuthError = { code: gaxiosError.response?.status, - reason: - gaxiosError.response?.data?.error || - gaxiosError.response?.data?.error_description || - 'Unknown reason', + reason: gaxiosError.response?.data?.error || 'Unknown reason', message: gaxiosError.response?.data?.error_description || gaxiosError.message || 'Unknown error', }; - switch (googleOAuthError.code) { - case 400: - if (googleOAuthError.reason === 'invalid_grant') { - return new ConnectedAccountRefreshAccessTokenException( - googleOAuthError.message, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, - ); - } - - return new ConnectedAccountRefreshAccessTokenException( - googleOAuthError.message, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, - ); - - case 401: - return new ConnectedAccountRefreshAccessTokenException( - googleOAuthError.message, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, - ); - - case 403: - return new ConnectedAccountRefreshAccessTokenException( - googleOAuthError.message, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, - ); - - case 429: - return new ConnectedAccountRefreshAccessTokenException( - googleOAuthError.message, - ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, - ); - - case 500: - case 502: - case 503: - case 504: - return new ConnectedAccountRefreshAccessTokenException( - `${googleOAuthError.code} - ${googleOAuthError.message}`, - ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, - ); - - default: - break; + if (GOOGLE_PERMANENT_OAUTH_ERROR_CODES.has(googleOAuthError.reason)) { + return new ConnectedAccountRefreshAccessTokenException( + `Google auth error: ${googleOAuthError.reason} - ${googleOAuthError.message}`, + ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, + ); } return new ConnectedAccountRefreshAccessTokenException( - `Google refresh token failed: ${googleOAuthError.message}`, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, + `Google refresh token failed (${googleOAuthError.code ?? 'no status'}): ${googleOAuthError.reason} - ${googleOAuthError.message}`, + ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, ); }; diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/constants/microsoft-permanent-auth-error-codes.constant.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/constants/microsoft-permanent-auth-error-codes.constant.ts new file mode 100644 index 0000000000..df9d04b7e7 --- /dev/null +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/constants/microsoft-permanent-auth-error-codes.constant.ts @@ -0,0 +1,9 @@ +/** + * @see https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes + */ +export const MICROSOFT_PERMANENT_AUTH_ERROR_CODES = new Set([ + 'invalid_grant', + 'invalid_client', + 'unauthorized_client', + 'invalid_request', +]); diff --git a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/utils/parse-msal-error.util.ts b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/utils/parse-msal-error.util.ts index b1a275ff64..6afab96c20 100644 --- a/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/utils/parse-msal-error.util.ts +++ b/packages/twenty-server/src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/utils/parse-msal-error.util.ts @@ -1,34 +1,10 @@ -import { - AuthError, - InteractionRequiredAuthError, - ServerError, -} from '@azure/msal-node'; +import { AuthError, InteractionRequiredAuthError } from '@azure/msal-node'; import { ConnectedAccountRefreshAccessTokenException, ConnectedAccountRefreshAccessTokenExceptionCode, } from 'src/engine/metadata-modules/connected-account/exceptions/connected-account-refresh-tokens.exception'; - -/** - * @see https://learn.microsoft.com/en-us/entra/identity-platform/reference-error-codes - */ -const PERMANENT_AUTH_ERROR_CODES = new Set([ - 'invalid_grant', - 'invalid_client', - 'unauthorized_client', - 'invalid_request', -]); - -/** - * @see https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-common/src/error/ClientAuthErrorCodes.ts - */ -const TRANSIENT_AUTH_ERROR_CODES = new Set([ - 'network_error', - 'no_network_connectivity', - 'endpoints_resolution_error', - 'openid_config_error', - 'request_cannot_be_made', -]); +import { MICROSOFT_PERMANENT_AUTH_ERROR_CODES } from 'src/modules/connected-account/refresh-tokens-manager/drivers/microsoft/constants/microsoft-permanent-auth-error-codes.constant'; export const parseMsalError = ( error: unknown, @@ -40,44 +16,20 @@ export const parseMsalError = ( ); } - if (error instanceof ServerError) { - const status = error.status; - - if (status === 429) { - return new ConnectedAccountRefreshAccessTokenException( - 'Microsoft rate limit exceeded', - ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, - ); - } - - if (status && status >= 500 && status < 600) { - return new ConnectedAccountRefreshAccessTokenException( - `Microsoft server error (${status}): ${error.errorMessage}`, - ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, - ); - } - } - - if (error instanceof AuthError) { - if (TRANSIENT_AUTH_ERROR_CODES.has(error.errorCode)) { - return new ConnectedAccountRefreshAccessTokenException( - `Microsoft network error: ${error.errorCode} - ${error.errorMessage}`, - ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, - ); - } - - if (PERMANENT_AUTH_ERROR_CODES.has(error.errorCode)) { - return new ConnectedAccountRefreshAccessTokenException( - `Microsoft auth error: ${error.errorCode} - ${error.errorMessage}`, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, - ); - } + if ( + error instanceof AuthError && + MICROSOFT_PERMANENT_AUTH_ERROR_CODES.has(error.errorCode) + ) { + return new ConnectedAccountRefreshAccessTokenException( + `Microsoft auth error: ${error.errorCode} - ${error.errorMessage}`, + ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, + ); } const message = error instanceof Error ? error.message : String(error); return new ConnectedAccountRefreshAccessTokenException( `Microsoft token refresh failed: ${message}`, - ConnectedAccountRefreshAccessTokenExceptionCode.INVALID_REFRESH_TOKEN, + ConnectedAccountRefreshAccessTokenExceptionCode.TEMPORARY_NETWORK_ERROR, ); };