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
3 changes: 3 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
24 changes: 23 additions & 1 deletion src/libs/Authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -62,6 +63,12 @@ function Authenticate(parameters: Parameters): Promise<Response | void> {
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,
});
Expand Down Expand Up @@ -147,12 +154,27 @@ function reauthenticate(command = ''): Promise<boolean> {
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,
Expand Down
45 changes: 45 additions & 0 deletions src/libs/telemetry/trackAuthenticationError.ts
Original file line number Diff line number Diff line change
@@ -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<string, string> = {
[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<string, unknown> = {
...(command && {command}),
...(commandName && {commandName}),
...(errorMessage && {errorMessage}),
...(providedParameters && {providedParameters}),
};

Sentry.captureException(error, {tags, extra});
}

export default trackAuthenticationError;
Loading