migrate webauthn.js to typescript
This commit is contained in:
Generated
+8
@@ -39,6 +39,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node22": "^22.0.0",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
|
"@types/cbor-js": "^0.1.1",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/git-rev-sync": "^2.0.2",
|
"@types/git-rev-sync": "^2.0.2",
|
||||||
"@types/node": "^22.10.2",
|
"@types/node": "^22.10.2",
|
||||||
@@ -4183,6 +4184,13 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/cbor-js": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/cbor-js/-/cbor-js-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-pfCx/EZC7VNBThwAQ0XvGPOXYm8BUk+gSVonaIGcEKBuqGJHTdcwAGW8WZkdRs/u9n9yOt1pBoPTCS1s8ZYpEQ==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@types/crypto-js": {
|
"node_modules/@types/crypto-js": {
|
||||||
"version": "4.2.2",
|
"version": "4.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tsconfig/node22": "^22.0.0",
|
"@tsconfig/node22": "^22.0.0",
|
||||||
|
"@types/cbor-js": "^0.1.1",
|
||||||
"@types/crypto-js": "^4.2.2",
|
"@types/crypto-js": "^4.2.2",
|
||||||
"@types/git-rev-sync": "^2.0.2",
|
"@types/git-rev-sync": "^2.0.2",
|
||||||
"@types/node": "^22.10.2",
|
"@types/node": "^22.10.2",
|
||||||
|
|||||||
Vendored
+9
@@ -21,3 +21,12 @@ interface Window {
|
|||||||
interface Navigator {
|
interface Navigator {
|
||||||
browserLanguage?: string;
|
browserLanguage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface Credential {
|
||||||
|
rawId: ArrayBuffer;
|
||||||
|
response: {
|
||||||
|
clientDataJSON: ArrayBuffer;
|
||||||
|
attestationObject: ArrayBuffer;
|
||||||
|
userHandle: ArrayBuffer;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
+1
-1
@@ -237,7 +237,7 @@ export function base64encode(arrayBuffer: ArrayBuffer): string | null {
|
|||||||
return btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(arrayBuffer))));
|
return btoa(String.fromCharCode.apply(null, Array.from(new Uint8Array(arrayBuffer))));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function base64decode(str: string): string | null {
|
export function base64decode(str: string): string {
|
||||||
if (!str) {
|
if (!str) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import CBOR from 'cbor-js';
|
import CBOR from 'cbor-js';
|
||||||
import logger from './logger.ts';
|
|
||||||
|
import type { ApplicationLockState } from '@/core/setting.ts';
|
||||||
|
import type { UserBasicInfo } from '@/models/user.ts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
isFunction,
|
isFunction,
|
||||||
stringToArrayBuffer,
|
stringToArrayBuffer,
|
||||||
@@ -10,8 +13,36 @@ import {
|
|||||||
import {
|
import {
|
||||||
generateRandomString
|
generateRandomString
|
||||||
} from './misc.ts';
|
} from './misc.ts';
|
||||||
|
import logger from './logger.ts';
|
||||||
|
|
||||||
const publicKeyCredentialCreationOptionsBaseTemplate = {
|
interface ClientData {
|
||||||
|
challenge: string;
|
||||||
|
crossOrigin: boolean;
|
||||||
|
origin: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AttestationData {
|
||||||
|
authData: Uint8Array;
|
||||||
|
fmt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WebAuthnRegisterResponse {
|
||||||
|
id: string | null;
|
||||||
|
clientData: ClientData;
|
||||||
|
publicKey: Uint8Array | null;
|
||||||
|
rawCredential: Credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WebAuthnVerifyResponse {
|
||||||
|
id: string | null;
|
||||||
|
userName: string;
|
||||||
|
userSecret: string;
|
||||||
|
clientData: ClientData;
|
||||||
|
rawCredential: Credential;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PUBLIC_KEY_CREDENTIAL_CREATION_OPTIONS_BASE_TEMPLATE = {
|
||||||
attestation: "none",
|
attestation: "none",
|
||||||
authenticatorSelection: {
|
authenticatorSelection: {
|
||||||
authenticatorAttachment: 'platform',
|
authenticatorAttachment: 'platform',
|
||||||
@@ -26,7 +57,7 @@ const publicKeyCredentialCreationOptionsBaseTemplate = {
|
|||||||
timeout: 120000
|
timeout: 120000
|
||||||
};
|
};
|
||||||
|
|
||||||
const publicKeyCredentialRequestOptionsBaseTemplate = {
|
const PUBLIC_KEY_CREDENTIAL_REQUEST_OPTIONS_BASE_TEMPLATE = {
|
||||||
allowCredentials: [{
|
allowCredentials: [{
|
||||||
type: 'public-key'
|
type: 'public-key'
|
||||||
}],
|
}],
|
||||||
@@ -34,37 +65,57 @@ const publicKeyCredentialRequestOptionsBaseTemplate = {
|
|||||||
timeout: 120000
|
timeout: 120000
|
||||||
};
|
};
|
||||||
|
|
||||||
function isSupported() {
|
function parseClientData(credential: Credential): ClientData | null {
|
||||||
|
const utf8Decoder = new TextDecoder('utf-8');
|
||||||
|
const decodedClientData = utf8Decoder.decode(credential.response.clientDataJSON);
|
||||||
|
return JSON.parse(decodedClientData) as ClientData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePublicKeyFromAttestationData(credential: Credential): Uint8Array {
|
||||||
|
const decodedAttestationData = CBOR.decode(credential.response.attestationObject) as AttestationData;
|
||||||
|
const authData = decodedAttestationData.authData;
|
||||||
|
|
||||||
|
const dataView = new DataView(new ArrayBuffer(2));
|
||||||
|
const idLenBytes = authData.slice(53, 55);
|
||||||
|
idLenBytes.forEach((value, index) => dataView.setUint8(index, value));
|
||||||
|
|
||||||
|
const credentialIdLength = dataView.getUint16(0);
|
||||||
|
const publicKeyBytes = authData.slice(55 + credentialIdLength);
|
||||||
|
|
||||||
|
return publicKeyBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isWebAuthnSupported(): boolean {
|
||||||
return !!window.PublicKeyCredential
|
return !!window.PublicKeyCredential
|
||||||
&& !!navigator.credentials
|
&& !!navigator.credentials
|
||||||
&& isFunction(window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable);
|
&& isFunction(window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable);
|
||||||
}
|
}
|
||||||
|
|
||||||
function isCompletelySupported() {
|
export function isWebAuthnCompletelySupported(): Promise<boolean> {
|
||||||
if (!isSupported()) {
|
if (!isWebAuthnSupported()) {
|
||||||
return Promise.resolve(false);
|
return Promise.resolve(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
return window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerCredential({ username, secret }, { nickname }) {
|
export function registerWebAuthnCredential(lockState: ApplicationLockState, userInfo: UserBasicInfo): Promise<WebAuthnRegisterResponse> {
|
||||||
if (!window.location || !window.location.hostname) {
|
if (!window.location || !window.location.hostname) {
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
notSupported: true
|
notSupported: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSupported() || !navigator.credentials.create) {
|
if (!isWebAuthnSupported() || !navigator.credentials.create) {
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
notSupported: true
|
notSupported: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const challenge = generateRandomString();
|
const challenge = generateRandomString();
|
||||||
const userId = `${username}|${secret}`; // username 32bytes(max) + secret 24bytes = 56bytes(max)
|
const userId = `${lockState.username}|${lockState.secret}`; // username 32bytes(max) + secret 24bytes = 56bytes(max)
|
||||||
|
|
||||||
const publicKeyCredentialCreationOptions = Object.assign({}, publicKeyCredentialCreationOptionsBaseTemplate, {
|
const publicKeyCredentialCreationOptions: PublicKeyCredentialCreationOptions = Object.assign({}, PUBLIC_KEY_CREDENTIAL_CREATION_OPTIONS_BASE_TEMPLATE, {
|
||||||
challenge: stringToArrayBuffer(challenge),
|
challenge: stringToArrayBuffer(challenge),
|
||||||
rp: {
|
rp: {
|
||||||
name: window.location.hostname,
|
name: window.location.hostname,
|
||||||
@@ -72,10 +123,10 @@ function registerCredential({ username, secret }, { nickname }) {
|
|||||||
},
|
},
|
||||||
user: {
|
user: {
|
||||||
id: stringToArrayBuffer(userId),
|
id: stringToArrayBuffer(userId),
|
||||||
name: username,
|
name: lockState.username,
|
||||||
displayName: nickname
|
displayName: userInfo.nickname
|
||||||
}
|
}
|
||||||
});
|
}) as PublicKeyCredentialCreationOptions;
|
||||||
|
|
||||||
logger.debug('webauthn create options', publicKeyCredentialCreationOptions);
|
logger.debug('webauthn create options', publicKeyCredentialCreationOptions);
|
||||||
|
|
||||||
@@ -91,7 +142,7 @@ function registerCredential({ username, secret }, { nickname }) {
|
|||||||
|
|
||||||
if (rawCredential && rawCredential.rawId &&
|
if (rawCredential && rawCredential.rawId &&
|
||||||
clientData && clientData.type === 'webauthn.create' && challengeFromClientData === challenge) {
|
clientData && clientData.type === 'webauthn.create' && challengeFromClientData === challenge) {
|
||||||
const ret = {
|
const ret: WebAuthnRegisterResponse = {
|
||||||
id: base64encode(rawCredential.rawId),
|
id: base64encode(rawCredential.rawId),
|
||||||
clientData: clientData,
|
clientData: clientData,
|
||||||
publicKey: publicKey,
|
publicKey: publicKey,
|
||||||
@@ -109,45 +160,28 @@ function registerCredential({ username, secret }, { nickname }) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseClientData(credential) {
|
export function verifyWebAuthnCredential(userInfo: UserBasicInfo, credentialId: string): Promise<WebAuthnVerifyResponse> {
|
||||||
const utf8Decoder = new TextDecoder('utf-8');
|
|
||||||
const decodedClientData = utf8Decoder.decode(credential.response.clientDataJSON);
|
|
||||||
return JSON.parse(decodedClientData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePublicKeyFromAttestationData(credential) {
|
|
||||||
const decodedAttestationData = CBOR.decode(credential.response.attestationObject);
|
|
||||||
const authData = decodedAttestationData.authData;
|
|
||||||
|
|
||||||
const dataView = new DataView(new ArrayBuffer(2));
|
|
||||||
const idLenBytes = authData.slice(53, 55);
|
|
||||||
idLenBytes.forEach((value, index) => dataView.setUint8(index, value));
|
|
||||||
|
|
||||||
const credentialIdLength = dataView.getUint16();
|
|
||||||
const publicKeyBytes = authData.slice(55 + credentialIdLength);
|
|
||||||
|
|
||||||
return publicKeyBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function verifyCredential({ username }, credentialId) {
|
|
||||||
if (!window.location || !window.location.hostname) {
|
if (!window.location || !window.location.hostname) {
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
notSupported: true
|
notSupported: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSupported() || !navigator.credentials.get) {
|
if (!isWebAuthnSupported() || !navigator.credentials.get) {
|
||||||
return Promise.reject({
|
return Promise.reject({
|
||||||
notSupported: true
|
notSupported: true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const challenge = generateRandomString();
|
const challenge = generateRandomString();
|
||||||
const publicKeyCredentialRequestOptions = Object.assign({}, publicKeyCredentialRequestOptionsBaseTemplate, {
|
const publicKeyCredentialRequestOptions: PublicKeyCredentialRequestOptions = Object.assign({}, PUBLIC_KEY_CREDENTIAL_REQUEST_OPTIONS_BASE_TEMPLATE, {
|
||||||
challenge: stringToArrayBuffer(challenge),
|
challenge: stringToArrayBuffer(challenge),
|
||||||
rpId: window.location.hostname
|
rpId: window.location.hostname
|
||||||
});
|
}) as PublicKeyCredentialRequestOptions;
|
||||||
publicKeyCredentialRequestOptions.allowCredentials[0].id = stringToArrayBuffer(base64decode(credentialId));
|
|
||||||
|
if (publicKeyCredentialRequestOptions.allowCredentials && publicKeyCredentialRequestOptions.allowCredentials.length > 0) {
|
||||||
|
publicKeyCredentialRequestOptions.allowCredentials[0].id = stringToArrayBuffer(base64decode(credentialId));
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug('webauthn get options', publicKeyCredentialRequestOptions);
|
logger.debug('webauthn get options', publicKeyCredentialRequestOptions);
|
||||||
|
|
||||||
@@ -162,8 +196,8 @@ function verifyCredential({ username }, credentialId) {
|
|||||||
|
|
||||||
if (rawCredential && rawCredential.rawId &&
|
if (rawCredential && rawCredential.rawId &&
|
||||||
clientData && clientData.type === 'webauthn.get' && challengeFromClientData === challenge &&
|
clientData && clientData.type === 'webauthn.get' && challengeFromClientData === challenge &&
|
||||||
userIdParts && userIdParts.length === 2 && userIdParts[0] === username) {
|
userIdParts && userIdParts.length === 2 && userIdParts[0] === userInfo.username) {
|
||||||
const ret = {
|
const ret: WebAuthnVerifyResponse = {
|
||||||
id: base64encode(rawCredential.rawId),
|
id: base64encode(rawCredential.rawId),
|
||||||
userName: userIdParts[0],
|
userName: userIdParts[0],
|
||||||
userSecret: userIdParts[1],
|
userSecret: userIdParts[1],
|
||||||
@@ -181,10 +215,3 @@ function verifyCredential({ username }, credentialId) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
|
||||||
isSupported,
|
|
||||||
isCompletelySupported,
|
|
||||||
registerCredential,
|
|
||||||
verifyCredential
|
|
||||||
}
|
|
||||||
@@ -119,8 +119,10 @@ import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
|||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
import { ThemeType } from '@/core/theme.ts';
|
import { ThemeType } from '@/core/theme.ts';
|
||||||
import logger from '@/lib/logger.ts';
|
import {
|
||||||
import webauthn from '@/lib/webauthn.js';
|
isWebAuthnSupported,
|
||||||
|
verifyWebAuthnCredential
|
||||||
|
} from '@/lib/webauthn.ts';
|
||||||
import {
|
import {
|
||||||
unlockTokenByWebAuthn,
|
unlockTokenByWebAuthn,
|
||||||
unlockTokenByPinCode,
|
unlockTokenByPinCode,
|
||||||
@@ -128,6 +130,7 @@ import {
|
|||||||
getWebAuthnCredentialId
|
getWebAuthnCredentialId
|
||||||
} from '@/lib/userstate.ts';
|
} from '@/lib/userstate.ts';
|
||||||
import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
||||||
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -150,7 +153,7 @@ export default {
|
|||||||
isWebAuthnAvailable() {
|
isWebAuthnAvailable() {
|
||||||
return this.settingsStore.appSettings.applicationLockWebAuthn
|
return this.settingsStore.appSettings.applicationLockWebAuthn
|
||||||
&& hasWebAuthnConfig()
|
&& hasWebAuthnConfig()
|
||||||
&& webauthn.isSupported();
|
&& isWebAuthnSupported();
|
||||||
},
|
},
|
||||||
isDarkMode() {
|
isDarkMode() {
|
||||||
return this.globalTheme.global.name.value === ThemeType.Dark;
|
return this.globalTheme.global.name.value === ThemeType.Dark;
|
||||||
@@ -175,14 +178,14 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!webauthn.isSupported()) {
|
if (!isWebAuthnSupported()) {
|
||||||
self.$refs.snackbar.showMessage('WebAuth is not supported on this device');
|
self.$refs.snackbar.showMessage('WebAuth is not supported on this device');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.verifyingByWebAuthn = true;
|
self.verifyingByWebAuthn = true;
|
||||||
|
|
||||||
webauthn.verifyCredential(
|
verifyWebAuthnCredential(
|
||||||
self.userStore.currentUserBasicInfo,
|
self.userStore.currentUserBasicInfo,
|
||||||
getWebAuthnCredentialId()
|
getWebAuthnCredentialId()
|
||||||
).then(({ id, userName, userSecret }) => {
|
).then(({ id, userName, userSecret }) => {
|
||||||
|
|||||||
@@ -66,8 +66,10 @@ import { useSettingsStore } from '@/stores/setting.ts';
|
|||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||||
|
|
||||||
import logger from '@/lib/logger.ts';
|
import {
|
||||||
import webauthn from '@/lib/webauthn.js';
|
isWebAuthnCompletelySupported,
|
||||||
|
registerWebAuthnCredential
|
||||||
|
} from '@/lib/webauthn.ts';
|
||||||
import {
|
import {
|
||||||
getUserAppLockState,
|
getUserAppLockState,
|
||||||
encryptToken,
|
encryptToken,
|
||||||
@@ -76,6 +78,7 @@ import {
|
|||||||
saveWebAuthnConfig,
|
saveWebAuthnConfig,
|
||||||
clearWebAuthnConfig
|
clearWebAuthnConfig
|
||||||
} from '@/lib/userstate.ts';
|
} from '@/lib/userstate.ts';
|
||||||
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -114,7 +117,7 @@ export default {
|
|||||||
if (newValue) {
|
if (newValue) {
|
||||||
self.enablingWebAuthn = true;
|
self.enablingWebAuthn = true;
|
||||||
|
|
||||||
webauthn.registerCredential(
|
registerWebAuthnCredential(
|
||||||
getUserAppLockState(),
|
getUserAppLockState(),
|
||||||
self.userStore.currentUserBasicInfo,
|
self.userStore.currentUserBasicInfo,
|
||||||
).then(({ id }) => {
|
).then(({ id }) => {
|
||||||
@@ -150,7 +153,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
const self = this;
|
const self = this;
|
||||||
webauthn.isCompletelySupported().then(result => {
|
isWebAuthnCompletelySupported().then(result => {
|
||||||
self.isSupportedWebAuthn = result;
|
self.isSupportedWebAuthn = result;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ import { useSettingsStore } from '@/stores/setting.ts';
|
|||||||
import { useUserStore } from '@/stores/user.ts';
|
import { useUserStore } from '@/stores/user.ts';
|
||||||
import { useTransactionsStore } from '@/stores/transaction.js';
|
import { useTransactionsStore } from '@/stores/transaction.js';
|
||||||
|
|
||||||
import logger from '@/lib/logger.ts';
|
import {
|
||||||
import webauthn from '@/lib/webauthn.js';
|
isWebAuthnCompletelySupported,
|
||||||
|
registerWebAuthnCredential
|
||||||
|
} from '@/lib/webauthn.ts';
|
||||||
import {
|
import {
|
||||||
getUserAppLockState,
|
getUserAppLockState,
|
||||||
encryptToken,
|
encryptToken,
|
||||||
@@ -51,6 +53,7 @@ import {
|
|||||||
saveWebAuthnConfig,
|
saveWebAuthnConfig,
|
||||||
clearWebAuthnConfig
|
clearWebAuthnConfig
|
||||||
} from '@/lib/userstate.ts';
|
} from '@/lib/userstate.ts';
|
||||||
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
@@ -88,7 +91,7 @@ export default {
|
|||||||
if (newValue) {
|
if (newValue) {
|
||||||
self.$showLoading();
|
self.$showLoading();
|
||||||
|
|
||||||
webauthn.registerCredential(
|
registerWebAuthnCredential(
|
||||||
getUserAppLockState(),
|
getUserAppLockState(),
|
||||||
self.userStore.currentUserBasicInfo,
|
self.userStore.currentUserBasicInfo,
|
||||||
).then(({ id }) => {
|
).then(({ id }) => {
|
||||||
@@ -124,7 +127,7 @@ export default {
|
|||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
const self = this;
|
const self = this;
|
||||||
webauthn.isCompletelySupported().then(result => {
|
isWebAuthnCompletelySupported().then(result => {
|
||||||
self.isSupportedWebAuthn = result;
|
self.isSupportedWebAuthn = result;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -74,8 +74,10 @@ import { useTransactionsStore } from '@/stores/transaction.js';
|
|||||||
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
import { useExchangeRatesStore } from '@/stores/exchangeRates.ts';
|
||||||
|
|
||||||
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
import { APPLICATION_LOGO_PATH } from '@/consts/asset.ts';
|
||||||
import logger from '@/lib/logger.ts';
|
import {
|
||||||
import webauthn from '@/lib/webauthn.js';
|
isWebAuthnSupported,
|
||||||
|
verifyWebAuthnCredential
|
||||||
|
} from '@/lib/webauthn.ts';
|
||||||
import {
|
import {
|
||||||
unlockTokenByWebAuthn,
|
unlockTokenByWebAuthn,
|
||||||
unlockTokenByPinCode,
|
unlockTokenByPinCode,
|
||||||
@@ -84,6 +86,7 @@ import {
|
|||||||
} from '@/lib/userstate.ts';
|
} from '@/lib/userstate.ts';
|
||||||
import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
import { setExpenseAndIncomeAmountColor } from '@/lib/ui/common.ts';
|
||||||
import { isModalShowing } from '@/lib/ui/mobile.ts';
|
import { isModalShowing } from '@/lib/ui/mobile.ts';
|
||||||
|
import logger from '@/lib/logger.ts';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: [
|
props: [
|
||||||
@@ -108,7 +111,7 @@ export default {
|
|||||||
isWebAuthnAvailable() {
|
isWebAuthnAvailable() {
|
||||||
return this.settingsStore.appSettings.applicationLockWebAuthn
|
return this.settingsStore.appSettings.applicationLockWebAuthn
|
||||||
&& hasWebAuthnConfig()
|
&& hasWebAuthnConfig()
|
||||||
&& webauthn.isSupported();
|
&& isWebAuthnSupported();
|
||||||
},
|
},
|
||||||
currentLanguageCode() {
|
currentLanguageCode() {
|
||||||
return this.$locale.getCurrentLanguageTag();
|
return this.$locale.getCurrentLanguageTag();
|
||||||
@@ -127,14 +130,14 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!webauthn.isSupported()) {
|
if (!isWebAuthnSupported()) {
|
||||||
self.$toast('WebAuth is not supported on this device');
|
self.$toast('WebAuth is not supported on this device');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.$showLoading();
|
self.$showLoading();
|
||||||
|
|
||||||
webauthn.verifyCredential(
|
verifyWebAuthnCredential(
|
||||||
self.userStore.currentUserBasicInfo,
|
self.userStore.currentUserBasicInfo,
|
||||||
getWebAuthnCredentialId()
|
getWebAuthnCredentialId()
|
||||||
).then(({ id, userName, userSecret }) => {
|
).then(({ id, userName, userSecret }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user