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
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.2/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.3/schema.json",
"vcs": {
"enabled": false,
"clientKind": "git",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getVersion } from '@tauri-apps/api/app';
import { listen, type UnlistenFn } from '@tauri-apps/api/event';
import { error } from '@tauri-apps/plugin-log';
import { useEffect, useState } from 'react';

import { clientApi } from '../../pages/client/clientAPI/clientApi.ts';
import { useClientStore } from '../../pages/client/hooks/useClientStore';
import { TauriEventKey } from '../../pages/client/types';
Expand Down Expand Up @@ -71,16 +71,20 @@ export const ApplicationUpdateManager = () => {
const getNewVersion = async (appVersion: string) => {
if (!appVersion) return;

const response = await getLatestAppVersion();

setApplicationUpdateData({
currentVersion: appVersion,
latestVersion: response.version,
releaseDate: response.release_date,
releaseNotesUrl: response.release_notes_url,
updateUrl: response.update_url,
dismissed: false,
});
try {
const response = await getLatestAppVersion();

setApplicationUpdateData({
currentVersion: appVersion,
latestVersion: response.version,
releaseDate: response.release_date,
releaseNotesUrl: response.release_notes_url,
updateUrl: response.update_url,
dismissed: false,
});
} catch (e) {
error(`Failed to check latest app version: ${e}`);
}
};

getNewVersion(appVersion);
Expand Down
2 changes: 2 additions & 0 deletions src/i18n/en/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ If you want to disengage your VPN connection, simply press "deactivate".
mfaNotConfigured: 'Selected method has not been configured.',
mfaStartGeneric:
'Could not start MFA process. Please try again or contact administrator.',
mfaFinishGeneric:
'Could not finish MFA process. Please try again or contact administrator.',
instanceNotFound: 'Could not find instance.',
locationNotSpecified: 'Location is not specified.',
invalidCode:
Expand Down
8 changes: 8 additions & 0 deletions src/i18n/i18n-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1702,6 +1702,10 @@ type RootTranslation = {
* C​o​u​l​d​ ​n​o​t​ ​s​t​a​r​t​ ​M​F​A​ ​p​r​o​c​e​s​s​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​ ​o​r​ ​c​o​n​t​a​c​t​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​.
*/
mfaStartGeneric: string
/**
* C​o​u​l​d​ ​n​o​t​ ​f​i​n​i​s​h​ ​M​F​A​ ​p​r​o​c​e​s​s​.​ ​P​l​e​a​s​e​ ​t​r​y​ ​a​g​a​i​n​ ​o​r​ ​c​o​n​t​a​c​t​ ​a​d​m​i​n​i​s​t​r​a​t​o​r​.
*/
mfaFinishGeneric: string
/**
* C​o​u​l​d​ ​n​o​t​ ​f​i​n​d​ ​i​n​s​t​a​n​c​e​.
*/
Expand Down Expand Up @@ -3391,6 +3395,10 @@ export type TranslationFunctions = {
* Could not start MFA process. Please try again or contact administrator.
*/
mfaStartGeneric: () => LocalizedString
/**
* Could not finish MFA process. Please try again or contact administrator.
*/
mfaFinishGeneric: () => LocalizedString
/**
* Could not find instance.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,56 +117,62 @@ export const MFAModal = () => {
location_id: location.network_id,
};

const response = await fetch(mfaStartUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
CLIENT_VERSION_HEADER: platformInfo.client_version,
CLIENT_PLATFORM_HEADER: platformInfo.platform_info,
},
body: JSON.stringify(data),
});
try {
const response = await fetch(mfaStartUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
CLIENT_VERSION_HEADER: platformInfo.client_version,
CLIENT_PLATFORM_HEADER: platformInfo.platform_info,
},
body: JSON.stringify(data),
});

if (response.ok) {
const data = (await response.json()) as MFAStartResponse;

switch (method) {
case 0:
setScreen('authenticator_app');
break;
case 1:
setScreen('email');
break;
case 2:
setScreen('openid_login');
break;
case 4:
// just to be safe
if (!isPresent(data.challenge)) {
toaster.error('Unsupported response from proxy');
}
setScreen('mobile_approve');
break;
default:
toaster.error(localLL.errors.mfaStartGeneric());
if (response.ok) {
const data = (await response.json()) as MFAStartResponse;

switch (method) {
case 0:
setScreen('authenticator_app');
break;
case 1:
setScreen('email');
break;
case 2:
setScreen('openid_login');
break;
case 4:
// just to be safe
if (!isPresent(data.challenge)) {
toaster.error('Unsupported response from proxy');
}
setScreen('mobile_approve');
break;
default:
toaster.error(localLL.errors.mfaStartGeneric());
return;
}
setStartResponse(data);
return data;
} else {
const errorData = ((await response.json()) as unknown as MFAError).error;
error(`MFA failed to start with the following error: ${errorData}`);
if (method === 2) {
setScreen('openid_unavailable');
return;
}
setStartResponse(data);
return data;
} else {
const errorData = ((await response.json()) as unknown as MFAError).error;
error(`MFA failed to start with the following error: ${errorData}`);
if (method === 2) {
setScreen('openid_unavailable');
return;
}
}

if (errorData === 'selected MFA method not available') {
toaster.error(localLL.errors.mfaNotConfigured());
} else {
toaster.error(localLL.errors.mfaStartGeneric());
}
if (errorData === 'selected MFA method not available') {
toaster.error(localLL.errors.mfaNotConfigured());
} else {
toaster.error(localLL.errors.mfaStartGeneric());
}

return;
}
} catch (rej) {
error(`Failed to execute proxy request: ${rej}`);
toaster.error(localLL.errors.mfaStartGeneric());
return;
}
},
Expand Down Expand Up @@ -569,50 +575,56 @@ const MFACodeForm = ({ description, token, proxyUrl, resetState }: MFACodeForm)
);

const finishMFA = async (code: string) => {
if (!location) return toaster.error(localLL.errors.mfaStartGeneric());
if (!location) return toaster.error(localLL.errors.mfaFinishGeneric());

const data = { token, code: code };

const response = await fetch(`${proxyUrl + CLIENT_MFA_ENDPOINT}/finish`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
CLIENT_VERSION_HEADER: platformInfo.client_version,
CLIENT_PLATFORM_HEADER: platformInfo.platform_info,
},
body: JSON.stringify(data),
});

if (response.ok) {
closeModal();
const data = (await response.json()) as MFAFinishResponse;
await connect({
locationId: location?.id,
connectionType: location.connection_type,
presharedKey: data.preshared_key,
try {
const response = await fetch(`${proxyUrl + CLIENT_MFA_ENDPOINT}/finish`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
CLIENT_VERSION_HEADER: platformInfo.client_version,
CLIENT_PLATFORM_HEADER: platformInfo.platform_info,
},
body: JSON.stringify(data),
});
} else {
const data = (await response.json()) as unknown as MFAError;
const { error: errorMessage } = data;
let message = '';

if (errorMessage === 'Unauthorized') {
message = localLL.errors.invalidCode();
} else if (
errorMessage === 'invalid token' ||
errorMessage === 'login session not found'
) {
console.error(data);
toaster.error(localLL.errors.tokenExpired());
resetState();

if (response.ok) {
closeModal();
const data = (await response.json()) as MFAFinishResponse;
await connect({
locationId: location?.id,
connectionType: location.connection_type,
presharedKey: data.preshared_key,
});
} else {
const data = (await response.json()) as unknown as MFAError;
const { error: errorMessage } = data;
let message = '';

if (errorMessage === 'Unauthorized') {
message = localLL.errors.invalidCode();
} else if (
errorMessage === 'invalid token' ||
errorMessage === 'login session not found'
) {
console.error(data);
toaster.error(localLL.errors.tokenExpired());
resetState();
error(JSON.stringify(data));
return;
} else {
toaster.error(localLL.errors.mfaFinishGeneric());
}

setMFAError(message);
error(JSON.stringify(data));
return;
} else {
toaster.error(localLL.errors.mfaStartGeneric());
}

setMFAError(message);
error(JSON.stringify(data));
} catch (rej) {
error(`Failed to execute proxy request: ${rej}`);
toaster.error(localLL.errors.mfaFinishGeneric());
return;
}
};
Expand Down