Skip to content
Closed
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 @@ -2192,6 +2192,7 @@ const CONST = {
BAD_REQUEST: 400,
INVALID_SEARCH_QUERY: 401,
NOT_AUTHENTICATED: 407,
NEEDS_2FA_SETUP: 432,
EXP_ERROR: 666,
UNABLE_TO_RETRY: 'unableToRetry',
UPDATE_REQUIRED: 426,
Expand Down Expand Up @@ -2236,10 +2237,12 @@ const CONST = {
},
ERROR_TYPE: {
SOCKET: 'Expensify\\Auth\\Error\\Socket',
NEEDS_2FA_SETUP: 'Expensify\\Error\\Policy\\PolicyRequires2FA',
},
ERROR_TITLE: {
SOCKET: 'Issue connecting to database',
DUPLICATE_RECORD: '400 Unique Constraints Violation',
NEEDS_2FA_SETUP: 'Two-Factor Authentication required',
},
NETWORK: {
METHOD: {
Expand Down
20 changes: 19 additions & 1 deletion src/libs/Middleware/Reauthentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,13 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue)
return;
}

if (data.jsonCode === CONST.JSON_CODE.NOT_AUTHENTICATED) {
// Two server-side signals indicate the user must set up 2FA:
// 1. Server throws PolicyRequires2FA error, with 666 jsonCode with the specefic error title or error type.
// 2. Server rejects a stale normal type token whose account now requires 2FA with jsonCode 432.
const is2FASetupRequired =
(data.jsonCode === CONST.JSON_CODE.EXP_ERROR && (data.type === CONST.ERROR_TYPE.NEEDS_2FA_SETUP || data.title === CONST.ERROR_TITLE.NEEDS_2FA_SETUP)) ||
data.jsonCode === CONST.JSON_CODE.NEEDS_2FA_SETUP;
if (data.jsonCode === CONST.JSON_CODE.NOT_AUTHENTICATED || is2FASetupRequired) {
if (getIsOffline()) {
// If we are offline and somehow handling this response we do not want to reauthenticate
throw new Error('Unable to reauthenticate because we are offline');
Expand Down Expand Up @@ -100,6 +106,18 @@ const Reauthentication: Middleware = (response, request, isFromSequentialQueue)
return;
}

// Replaying the original request after successful re-authentication might hit the same server-side 2FA check and loop indefinitely. So we skip it.
// The re-reauth itself refreshes `account.needsTwoFactorAuthSetup` in Onyx via updates sent by server, which is enough to show the 2FA enforcement overlay.
if (is2FASetupRequired) {
if (isFromSequentialQueue) {
return data;
}
if (request.resolve) {
request.resolve(data);
}
return data;
}

if (isFromSequentialQueue || apiRequestType === CONST.API_REQUEST_TYPE.MAKE_REQUEST_WITH_SIDE_EFFECTS) {
return processWithMiddleware(request, isFromSequentialQueue);
}
Expand Down
Loading