From 81f334798113cfd6d5161edea3e00ac6cb603d36 Mon Sep 17 00:00:00 2001 From: Danny Coates Date: Fri, 8 Dec 2017 09:45:00 -0800 Subject: [PATCH] retry setPassword on first nonce failure. fixes #664 --- app/fileSender.js | 62 +++++++++++++++++++++++---------------- server/routes/password.js | 8 ++--- 2 files changed, 40 insertions(+), 30 deletions(-) diff --git a/app/fileSender.js b/app/fileSender.js index c4fc2e70..5b04b884 100644 --- a/app/fileSender.js +++ b/app/fileSender.js @@ -12,6 +12,33 @@ async function getAuthHeader(authKey, nonce) { return `send-v1 ${arrayToB64(new Uint8Array(sig))}`; } +async function sendPassword(file, authKey, rawAuth) { + const authHeader = await getAuthHeader(authKey, file.nonce); + return new Promise((resolve, reject) => { + const xhr = new XMLHttpRequest(); + xhr.onreadystatechange = () => { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + return resolve(xhr.response); + } + if (xhr.status === 401) { + const nonce = xhr.getResponseHeader('WWW-Authenticate').split(' ')[1]; + file.nonce = nonce; + } + reject(new Error(xhr.status)); + } + }; + xhr.onerror = () => reject(new Error(0)); + xhr.ontimeout = () => reject(new Error(0)); + xhr.open('post', `/api/password/${file.id}`); + xhr.setRequestHeader('Authorization', authHeader); + xhr.setRequestHeader('Content-Type', 'application/json'); + xhr.responseType = 'json'; + xhr.timeout = 2000; + xhr.send(JSON.stringify({ auth: arrayToB64(new Uint8Array(rawAuth)) })); + }); +} + export default class FileSender extends Nanobus { constructor(file) { super('FileSender'); @@ -259,7 +286,6 @@ export default class FileSender extends Nanobus { true, ['sign'] ); - const authHeader = await getAuthHeader(authKey, file.nonce); const pwdKey = await window.crypto.subtle.importKey( 'raw', encoder.encode(password), @@ -283,30 +309,14 @@ export default class FileSender extends Nanobus { ['sign'] ); const rawAuth = await window.crypto.subtle.exportKey('raw', newAuthKey); - return new Promise((resolve, reject) => { - const xhr = new XMLHttpRequest(); - xhr.onreadystatechange = () => { - if (xhr.readyState === XMLHttpRequest.DONE) { - if (xhr.status === 200) { - return resolve(xhr.response); - } - if (xhr.status === 401) { - const nonce = xhr - .getResponseHeader('WWW-Authenticate') - .split(' ')[1]; - file.nonce = nonce; - } - reject(new Error(xhr.status)); - } - }; - xhr.onerror = () => reject(new Error(0)); - xhr.ontimeout = () => reject(new Error(0)); - xhr.open('post', `/api/password/${file.id}`); - xhr.setRequestHeader('Authorization', authHeader); - xhr.setRequestHeader('Content-Type', 'application/json'); - xhr.responseType = 'json'; - xhr.timeout = 2000; - xhr.send(JSON.stringify({ auth: arrayToB64(new Uint8Array(rawAuth)) })); - }); + try { + await sendPassword(file, authKey, rawAuth); + } catch (e) { + if (e.message === '401' && file.nonce !== e.nonce) { + await sendPassword(file, authKey, rawAuth); + } else { + throw e; + } + } } } diff --git a/server/routes/password.js b/server/routes/password.js index 68d1125d..fbb01dfa 100644 --- a/server/routes/password.js +++ b/server/routes/password.js @@ -24,12 +24,12 @@ module.exports = async function(req, res) { res.set('WWW-Authenticate', `send-v1 ${meta.nonce}`); return res.sendStatus(401); } - const nonce = crypto.randomBytes(16).toString('base64'); - storage.setField(id, 'nonce', nonce); - res.set('WWW-Authenticate', `send-v1 ${nonce}`); } catch (e) { - res.sendStatus(404); + return res.sendStatus(404); } + const nonce = crypto.randomBytes(16).toString('base64'); + storage.setField(id, 'nonce', nonce); + res.set('WWW-Authenticate', `send-v1 ${nonce}`); storage.setField(id, 'auth', req.body.auth); storage.setField(id, 'pwd', 1); res.sendStatus(200);