From c28b7775a59d247bdfe21210ddf2d1b9f8450393 Mon Sep 17 00:00:00 2001 From: azivner Date: Sat, 28 Oct 2017 22:17:00 -0400 Subject: [PATCH] API auth (untested) --- app.js | 2 ++ routes/api/login.js | 38 ++++++++++++++++++++++++++++++++------ services/auth.js | 4 ++-- services/sync.js | 44 +++++++++++++++++++++++++++++++++++++------- 4 files changed, 73 insertions(+), 15 deletions(-) diff --git a/app.js b/app.js index 4d7532e2b..490091418 100644 --- a/app.js +++ b/app.js @@ -27,6 +27,7 @@ const settingsApiRoute = require('./routes/api/settings'); const passwordApiRoute = require('./routes/api/password'); const migrationApiRoute = require('./routes/api/migration'); const syncApiRoute = require('./routes/api/sync'); +const loginApiRoute = require('./routes/api/login'); const dataDir = require('./services/data_dir'); const sessionSecret = require('./services/session_secret'); @@ -94,6 +95,7 @@ app.use('/api/settings', settingsApiRoute); app.use('/api/password', passwordApiRoute); app.use('/api/migration', migrationApiRoute); app.use('/api/sync', syncApiRoute); +app.use('/api/login', loginApiRoute); // catch 404 and forward to error handler app.use((req, res, next) => { diff --git a/routes/api/login.js b/routes/api/login.js index 29ee1da3d..383e8c81b 100644 --- a/routes/api/login.js +++ b/routes/api/login.js @@ -2,17 +2,43 @@ const express = require('express'); const router = express.Router(); -const auth = require('../../services/auth'); const sql = require('../../services/sql'); -const migration = require('../../services/migration'); +const utils = require('../../services/utils'); +const crypto = require('crypto'); router.post('', async (req, res, next) => { + const timestamp = req.body.timestamp; + const now = utils.nowTimestamp(); - res.send({ - 'db_version': parseInt(await sql.getOption('db_version')), - 'app_db_version': migration.APP_DB_VERSION - }); + if (Math.abs(timestamp - now) > 5) { + res.status(400); + res.send({ message: 'Auth request time is out of sync' }); + } + + const dbVersion = res.body.dbVersion; + + if (dbVersion !== migration.APP_DB_VERSION) { + res.status(400); + res.send({ message: 'Non-matching db versions, local is version ' + migration.APP_DB_VERSION }); + } + + const documentSecret = await sql.getOption('document_secret'); + + const hmac = crypto.createHmac('sha256', documentSecret); + hmac.update(timestamp); + const expectedHash = hmac.digest('base64'); + + const givenHash = req.body.hash; + + if (expectedHash !== givenHash) { + res.status(400); + res.send({ message: "Hash doesn't match" }); + } + + req.session.loggedIn = true; + + res.send({}); }); module.exports = router; \ No newline at end of file diff --git a/services/auth.js b/services/auth.js index 3b07009f2..13e4d5802 100644 --- a/services/auth.js +++ b/services/auth.js @@ -25,7 +25,7 @@ async function checkAuthWithoutMigration(req, res, next) { } async function checkApiAuth(req, res, next) { - if (!req.session.loggedIn && req.header("auth") !== "sync") { + if (!req.session.loggedIn) { res.sendStatus(401); } @@ -38,7 +38,7 @@ async function checkApiAuth(req, res, next) { } async function checkApiAuthWithoutMigration(req, res, next) { - if (!req.session.loggedIn && req.header("auth") !== "sync") { + if (!req.session.loggedIn) { res.sendStatus(401); } else { diff --git a/services/sync.js b/services/sync.js index 4b8abf008..477187950 100644 --- a/services/sync.js +++ b/services/sync.js @@ -7,13 +7,14 @@ const migration = require('./migration'); const utils = require('./utils'); const config = require('./config'); const audit_category = require('./audit_category'); +const crypto = require('crypto'); const SYNC_SERVER = config['Sync']['syncServerHost']; let syncInProgress = false; -async function pullSync() { +async function pullSync(cookieJar) { const lastSyncedPull = parseInt(await sql.getOption('last_synced_pull')); const resp = await rp({ @@ -35,7 +36,8 @@ async function pullSync() { headers: { auth: 'sync' }, - json: true + json: true, + jar: cookieJar }); @@ -53,7 +55,7 @@ async function pullSync() { } } -async function pushSync() { +async function pushSync(cookieJar) { const lastSyncedPush = parseInt(await sql.getOption('last_synced_push')); const syncStarted = utils.nowTimestamp(); @@ -66,7 +68,8 @@ async function pushSync() { auth: 'sync' }, body: changed, - json: true + json: true, + jar: cookieJar }); for (const noteId of changed.notes) { @@ -79,13 +82,38 @@ async function pushSync() { auth: 'sync' }, body: note, - json: true + json: true, + jar: cookieJar }); } await sql.setOption('last_synced_push', syncStarted); } +async function login() { + const timestamp = utils.nowTimestamp(); + + const hmac = crypto.createHmac('sha256', documentSecret); + hmac.update(timestamp); + const hash = hmac.digest('base64'); + + const cookieJar = rp.jar(); + + await rp({ + method: 'POST', + uri: SYNC_SERVER + '/api/login', + body: { + timestamp: timestamp, + dbVersion: migration.APP_DB_VERSION, + hash: hash + }, + json: true, + jar: cookieJar + }); + + return cookieJar; +} + async function sync() { if (syncInProgress) { return; @@ -98,9 +126,11 @@ async function sync() { return; } - await pushSync(); + const cookieJar = await login(); - await pullSync(); + await pushSync(cookieJar); + + await pullSync(cookieJar); } catch (e) { log.error("sync failed: " + e.stack);