diff --git a/src/CONST/index.ts b/src/CONST/index.ts index 975cbd0052a7..c4babbaa864d 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -1682,6 +1682,9 @@ const CONST = { CONTEXT_POLICIES: 'Policies', TAG_ACTIVE_POLICY: 'active_policy_id', TAG_NUDGE_MIGRATION_COHORT: 'nudge_migration_cohort', + TAG_AUTHENTICATION_FUNCTION: 'authentication_function', + TAG_AUTHENTICATION_ERROR_TYPE: 'authentication_error_type', + TAG_AUTHENTICATION_JSON_CODE: 'authentication_json_code', // Span names SPAN_OPEN_REPORT: 'ManualOpenReport', SPAN_APP_STARTUP: 'ManualAppStartup', diff --git a/src/libs/Authentication.ts b/src/libs/Authentication.ts index 77f19dd6b3ef..51dcf99b42cd 100644 --- a/src/libs/Authentication.ts +++ b/src/libs/Authentication.ts @@ -15,6 +15,7 @@ import {post} from './Network'; import {getCredentials, hasReadRequiredDataFromStorage, setAuthToken, setIsAuthenticating} from './Network/NetworkStore'; import requireParameters from './requireParameters'; import {checkIfShouldUseNewPartnerName} from './SessionUtils'; +import trackAuthenticationError from './telemetry/trackAuthenticationError'; type Parameters = { useExpensifyLogin?: boolean; @@ -62,6 +63,12 @@ function Authenticate(parameters: Parameters): Promise { requireParameters(['partnerName', 'partnerPassword', 'partnerUserID', 'partnerUserSecret'], parameters, commandName); } catch (error) { const errorMessage = (error as Error).message; + trackAuthenticationError(error as Error, { + errorType: 'missing_params', + functionName: 'Authenticate', + commandName, + providedParameters: Object.keys(parameters), + }); Log.hmmm('Redirecting to Sign In because we failed to reauthenticate', { error: errorMessage, }); @@ -147,12 +154,27 @@ function reauthenticate(command = ''): Promise { if (response.jsonCode === CONST.JSON_CODE.UNABLE_TO_RETRY) { // When a fetch() fails due to a network issue and an error is thrown we won't log the user out. Most likely they // have a spotty connection and will need to retry reauthenticate when they come back online. Error so it can be handled by the retry mechanism. - throw new Error('Unable to retry Authenticate request'); + const error = new Error('Unable to retry Authenticate request'); + trackAuthenticationError(error, { + errorType: 'network_retry', + functionName: 'reauthenticate', + jsonCode: response.jsonCode, + command, + }); + throw error; } // If authentication fails and we are online then log the user out if (response.jsonCode !== 200) { const errorMessage = getAuthenticateErrorMessage(response); + + trackAuthenticationError(new Error('Authentication failed'), { + errorType: 'auth_failure', + functionName: 'reauthenticate', + jsonCode: response.jsonCode, + command, + errorMessage, + }); setIsAuthenticating(false); Log.hmmm('Redirecting to Sign In because we failed to reauthenticate', { command, diff --git a/src/libs/telemetry/trackAuthenticationError.ts b/src/libs/telemetry/trackAuthenticationError.ts new file mode 100644 index 000000000000..08438b776e94 --- /dev/null +++ b/src/libs/telemetry/trackAuthenticationError.ts @@ -0,0 +1,45 @@ +import * as Sentry from '@sentry/react-native'; +import CONST from '@src/CONST'; + +type AuthenticationFunction = 'Authenticate' | 'reauthenticate'; +type AuthenticationErrorType = 'missing_params' | 'network_retry' | 'auth_failure'; + +type AuthenticationErrorContext = { + errorType: AuthenticationErrorType; + functionName: AuthenticationFunction; + jsonCode?: number | string; + command?: string; + commandName?: string; + errorMessage?: string; + providedParameters?: string[]; +}; + +/** + * Track authentication errors in Sentry with extra context. + * + * @param error - The error object to capture + * @param context - Additional context about the authentication error + */ +function trackAuthenticationError(error: Error, context: AuthenticationErrorContext): void { + const {errorType, functionName, jsonCode, command, commandName, errorMessage, providedParameters} = context; + + const tags: Record = { + [CONST.TELEMETRY.TAG_AUTHENTICATION_FUNCTION]: functionName, + [CONST.TELEMETRY.TAG_AUTHENTICATION_ERROR_TYPE]: errorType, + }; + + if (jsonCode !== undefined) { + tags[CONST.TELEMETRY.TAG_AUTHENTICATION_JSON_CODE] = String(jsonCode); + } + + const extra: Record = { + ...(command && {command}), + ...(commandName && {commandName}), + ...(errorMessage && {errorMessage}), + ...(providedParameters && {providedParameters}), + }; + + Sentry.captureException(error, {tags, extra}); +} + +export default trackAuthenticationError;