diff --git a/src/lib/userstate.js b/src/lib/userstate.js index 9fa5d21e..5b24af52 100644 --- a/src/lib/userstate.js +++ b/src/lib/userstate.js @@ -13,7 +13,8 @@ const tokenSessionStorageKey = 'lab_user_session_token'; const appLockSecretSessionStorageKey = 'lab_user_app_lock_secret'; function getAppLockSecret(pinCode) { - return CryptoJS.SHA256(APP_LOCK_SECRET_BASE_STRING_PREFIX + pinCode).toString(); + const hashedPinCode = CryptoJS.SHA256(APP_LOCK_SECRET_BASE_STRING_PREFIX + pinCode).toString(); + return hashedPinCode.substr(0, 24); // put secret into user id of webauthn (user id total length must less 64 bytes) } function getEncryptedToken(token, secret) { diff --git a/src/lib/webauthn.js b/src/lib/webauthn.js index b3388268..c2f28f37 100644 --- a/src/lib/webauthn.js +++ b/src/lib/webauthn.js @@ -51,6 +51,8 @@ function registerCredential({ username, nickname }, userSecret) { } const challenge = utils.generateRandomString(); + const userId = `${username}|${userSecret}`; // username 32bytes(max) + userSecret 24bytes = 56bytes(max) + const publicKeyCredentialCreationOptions = Object.assign({}, PUBLIC_KEY_CREDENTIAL_CREATION_OPTIONS_TEMPLATE, { challenge: utils.stringToArrayBuffer(challenge), rp: { @@ -58,7 +60,7 @@ function registerCredential({ username, nickname }, userSecret) { id: window.location.hostname }, user: { - id: utils.stringToArrayBuffer(userSecret), + id: utils.stringToArrayBuffer(userId), name: username, displayName: nickname } @@ -116,7 +118,7 @@ function parsePublicKeyFromAttestationData(credential) { return publicKeyBytes; } -function verifyCredential(credentialId) { +function verifyCredential({ username }, credentialId) { if (!window.location || !window.location.hostname) { return Promise.reject({ notSupported: true @@ -143,16 +145,17 @@ function verifyCredential(credentialId) { }).then(rawCredential => { const clientData = rawCredential ? parseClientData(rawCredential) : null; const challengeFromClientData = clientData && clientData.challenge ? atob(clientData.challenge) : null; + const userIdParts = rawCredential && rawCredential.response && rawCredential.response.userHandle ? utils.arrayBufferToString(rawCredential.response.userHandle).split('|') : null; logger.debug('webauthn get raw response', rawCredential); if (rawCredential && rawCredential.rawId && - rawCredential.response && rawCredential.response.userHandle && - clientData && clientData.type === 'webauthn.get' && challengeFromClientData === challenge) { - + clientData && clientData.type === 'webauthn.get' && challengeFromClientData === challenge && + userIdParts && userIdParts.length === 2 && userIdParts[0] === username) { const ret = { id: utils.base64encode(rawCredential.rawId), - userSecret: utils.arrayBufferToString(rawCredential.response.userHandle), + userName: userIdParts[0], + userSecret: userIdParts[1], clientData: clientData, rawCredential: rawCredential }; diff --git a/src/views/mobile/Unlock.vue b/src/views/mobile/Unlock.vue index 1b7119d5..7bcd6553 100644 --- a/src/views/mobile/Unlock.vue +++ b/src/views/mobile/Unlock.vue @@ -33,6 +33,7 @@ export default { if (self.$settings.isEnableApplicationLockWebAuthn() && self.$user.getWebAuthnCredentialId()) { self.$webauthn.verifyCredential( + self.$user.getUserInfo(), self.$user.getWebAuthnCredentialId() ).then(({ id, userSecret }) => { self.$user.unlockTokenByWebAuthn(id, userSecret);