Skip to content
This repository was archived by the owner on Apr 3, 2019. It is now read-only.

Commit fdff3e9

Browse files
authored
feat(sessions): Add support for reauth on an existing session. (#305); r=philbooth
1 parent 9b8efcb commit fdff3e9

File tree

7 files changed

+293
-26
lines changed

7 files changed

+293
-26
lines changed

db-server/test/backend/db_tests.js

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ module.exports = function (config, DB) {
268268
assert(Array.isArray(sessions), 'sessions is an array')
269269
assert.equal(sessions.length, 1, 'sessions has one item')
270270

271-
assert.equal(Object.keys(sessions[0]).length, 18, 'session has correct properties')
271+
assert.equal(Object.keys(sessions[0]).length, 19, 'session has correct properties')
272272
assert.equal(sessions[0].tokenId.toString('hex'), sessionTokenData.tokenId.toString('hex'), 'tokenId is correct')
273273
assert.equal(sessions[0].uid.toString('hex'), accountData.uid.toString('hex'), 'uid is correct')
274274
assert.equal(sessions[0].createdAt, sessionTokenData.createdAt, 'createdAt is correct')
@@ -279,6 +279,7 @@ module.exports = function (config, DB) {
279279
assert.equal(sessions[0].uaDeviceType, sessionTokenData.uaDeviceType, 'uaDeviceType is correct')
280280
assert.equal(sessions[0].uaFormFactor, sessionTokenData.uaFormFactor, 'uaFormFactor is correct')
281281
assert.equal(sessions[0].lastAccessTime, sessionTokenData.createdAt, 'lastAccessTime is correct')
282+
assert.equal(sessions[0].authAt, sessionTokenData.createdAt, 'authAt is correct')
282283
})
283284
})
284285

@@ -311,6 +312,7 @@ module.exports = function (config, DB) {
311312
assert.equal(token.uaDeviceType, sessionTokenData.uaDeviceType, 'uaDeviceType is correct')
312313
assert.equal(token.uaFormFactor, sessionTokenData.uaFormFactor, 'uaFormFactor is correct')
313314
assert.equal(token.lastAccessTime, sessionTokenData.createdAt, 'lastAccessTime was set')
315+
assert.equal(token.authAt, sessionTokenData.createdAt, 'authAt is correct')
314316
assert.equal(!! token.emailVerified, accountData.emailVerified, 'token emailVerified is same as account emailVerified')
315317
assert.equal(token.email, accountData.email, 'token email same as account email')
316318
assert.deepEqual(token.emailCode, accountData.emailCode, 'token emailCode same as account emailCode')
@@ -326,7 +328,8 @@ module.exports = function (config, DB) {
326328
uaOS: 'bar',
327329
uaOSVersion: '2',
328330
uaDeviceType: 'baz',
329-
lastAccessTime: 42
331+
lastAccessTime: 42,
332+
authAt: 1234567
330333
}
331334
return db.updateSessionToken(sessionTokenData.tokenId, sessionTokenUpdates)
332335
.then((result) => {
@@ -344,6 +347,7 @@ module.exports = function (config, DB) {
344347
assert.equal(token.uaDeviceType, 'baz', 'uaDeviceType is correct')
345348
assert.equal(token.uaFormFactor, sessionTokenData.uaFormFactor, 'uaFormFactor is correct')
346349
assert.equal(token.lastAccessTime, 42, 'lastAccessTime is correct')
350+
assert.equal(token.authAt, 1234567, 'authAt is correct')
347351
assert.equal(!! token.emailVerified, accountData.emailVerified, 'token emailVerified is same as account emailVerified')
348352
assert.equal(token.email, accountData.email, 'token email same as account email')
349353
assert.deepEqual(token.emailCode, accountData.emailCode, 'token emailCode same as account emailCode')
@@ -354,6 +358,32 @@ module.exports = function (config, DB) {
354358
})
355359
})
356360

361+
it('should update mustVerify to true, but not to false', () => {
362+
return db.sessionTokenWithVerificationStatus(sessionTokenData.tokenId)
363+
.then((token) => {
364+
assert.equal(token.mustVerify, false, 'mustVerify starts out as false')
365+
assert.equal(token.uaBrowser, 'mock browser', 'other fields have their default values')
366+
return db.updateSessionToken(sessionTokenData.tokenId, { mustVerify: true })
367+
})
368+
.then((result) => {
369+
assert.deepEqual(result, {}, 'Returned an empty object on session token update')
370+
return db.sessionTokenWithVerificationStatus(sessionTokenData.tokenId)
371+
})
372+
.then((token) => {
373+
assert.equal(token.mustVerify, true, 'mustVerify was correctly updated to true')
374+
assert.equal(token.uaBrowser, 'mock browser', 'other fields were not updated')
375+
return db.updateSessionToken(sessionTokenData.tokenId, { mustVerify: false })
376+
})
377+
.then((result) => {
378+
assert.deepEqual(result, {}, 'Returned an empty object on session token update')
379+
return db.sessionTokenWithVerificationStatus(sessionTokenData.tokenId)
380+
})
381+
.then((token) => {
382+
assert.equal(token.mustVerify, true, 'mustVerify was not reset back to false')
383+
assert.equal(token.uaBrowser, 'mock browser', 'other fields were not updated')
384+
})
385+
})
386+
357387
it('should get verification state', () => {
358388
return db.sessionTokenWithVerificationStatus(sessionTokenData.tokenId)
359389
.then((token) => {
@@ -367,6 +397,7 @@ module.exports = function (config, DB) {
367397
assert.equal(token.uaDeviceType, sessionTokenData.uaDeviceType, 'uaDeviceType is correct')
368398
assert.equal(token.uaFormFactor, sessionTokenData.uaFormFactor, 'uaFormFactor is correct')
369399
assert.equal(token.lastAccessTime, sessionTokenData.createdAt, 'lastAccessTime was set')
400+
assert.equal(token.authAt, sessionTokenData.createdAt, 'authAt is correct')
370401
assert.equal(!! token.emailVerified, accountData.emailVerified, 'token emailVerified is same as account emailVerified')
371402
assert.equal(token.email, accountData.email, 'token email same as account email')
372403
assert.deepEqual(token.emailCode, accountData.emailCode, 'token emailCode same as account emailCode')

db-server/test/backend/remote.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,7 +345,7 @@ module.exports = function(cfg, makeServer) {
345345
respOk(r)
346346
var sessions = r.obj
347347
assert.equal(sessions.length, 1, 'sessions contains one item')
348-
assert.equal(Object.keys(sessions[0]).length, 18, 'session has correct properties')
348+
assert.equal(Object.keys(sessions[0]).length, 19, 'session has correct properties')
349349
assert.equal(sessions[0].tokenId, user.sessionTokenId, 'tokenId is correct')
350350
assert.equal(sessions[0].uid, user.accountId, 'uid is correct')
351351
assert.equal(sessions[0].createdAt, user.sessionToken.createdAt, 'createdAt is correct')
@@ -356,6 +356,7 @@ module.exports = function(cfg, makeServer) {
356356
assert.equal(sessions[0].uaDeviceType, user.sessionToken.uaDeviceType, 'uaDeviceType is correct')
357357
assert.equal(sessions[0].uaFormFactor, user.sessionToken.uaFormFactor, 'uaFormFactor is correct')
358358
assert.equal(sessions[0].lastAccessTime, user.sessionToken.createdAt, 'lastAccessTime is correct')
359+
assert.equal(sessions[0].authAt, user.sessionToken.createdAt, 'authAt is correct')
359360

360361
// Fetch the session token
361362
return client.getThen('/sessionToken/' + user.sessionTokenId)
@@ -373,6 +374,7 @@ module.exports = function(cfg, makeServer) {
373374
assert.equal(token.uaDeviceType, user.sessionToken.uaDeviceType, 'uaDeviceType matches')
374375
assert.equal(token.uaFormFactor, user.sessionToken.uaFormFactor, 'uaFormFactor matches')
375376
assert.equal(token.lastAccessTime, token.createdAt, 'lastAccessTime was set')
377+
assert.equal(token.authAt, token.createdAt, 'authAt was set to default')
376378
assert.equal(!! token.emailVerified, user.account.emailVerified, 'emailVerified same as account emailVerified')
377379
assert.equal(token.email, user.account.email, 'token.email same as account email')
378380
assert.deepEqual(token.emailCode, user.account.emailCode, 'token emailCode same as account emailCode')
@@ -397,6 +399,7 @@ module.exports = function(cfg, makeServer) {
397399
assert.equal(token.uaDeviceType, user.sessionToken.uaDeviceType, 'uaDeviceType matches')
398400
assert.equal(token.uaFormFactor, user.sessionToken.uaFormFactor, 'uaFormFactor matches')
399401
assert.equal(token.lastAccessTime, token.createdAt, 'lastAccessTime was set')
402+
assert.equal(token.authAt, token.createdAt, 'authAt was set to default')
400403
assert.equal(!! token.emailVerified, user.account.emailVerified, 'emailVerified same as account emailVerified')
401404
assert.equal(token.email, user.account.email, 'token.email same as account email')
402405
assert.deepEqual(token.emailCode, user.account.emailCode, 'token emailCode same as account emailCode')
@@ -427,6 +430,7 @@ module.exports = function(cfg, makeServer) {
427430
assert.equal(token.uaDeviceType, verifiedUser.sessionToken.uaDeviceType, 'uaDeviceType matches')
428431
assert.equal(token.uaFormFactor, verifiedUser.sessionToken.uaFormFactor, 'uaFormFactor matches')
429432
assert.equal(token.lastAccessTime, token.createdAt, 'lastAccessTime was set')
433+
assert.equal(token.authAt, token.createdAt, 'authAt was set to default')
430434
assert.equal(!! token.emailVerified, verifiedUser.account.emailVerified, 'emailVerified same as account emailVerified')
431435
assert.equal(token.email, verifiedUser.account.email, 'token.email same as account email')
432436
assert.deepEqual(token.emailCode, verifiedUser.account.emailCode, 'token emailCode same as account emailCode')
@@ -451,6 +455,7 @@ module.exports = function(cfg, makeServer) {
451455
assert.equal(token.uaDeviceType, verifiedUser.sessionToken.uaDeviceType, 'uaDeviceType matches')
452456
assert.equal(token.uaFormFactor, verifiedUser.sessionToken.uaFormFactor, 'uaFormFactor matches')
453457
assert.equal(token.lastAccessTime, token.createdAt, 'lastAccessTime was set')
458+
assert.equal(token.authAt, token.createdAt, 'authAt was set to default')
454459
assert.equal(!! token.emailVerified, verifiedUser.account.emailVerified, 'emailVerified same as account emailVerified')
455460
assert.equal(token.email, verifiedUser.account.email, 'token.email same as account email')
456461
assert.deepEqual(token.emailCode, verifiedUser.account.emailCode, 'token emailCode same as account emailCode')
@@ -517,7 +522,8 @@ module.exports = function(cfg, makeServer) {
517522
uaOS: 'different OS',
518523
uaOSVersion: 'different OS version',
519524
uaDeviceType: 'different device type',
520-
lastAccessTime: 42
525+
lastAccessTime: 42,
526+
authAt: 1234567
521527
})
522528
})
523529
.then(function(r) {
@@ -539,6 +545,7 @@ module.exports = function(cfg, makeServer) {
539545
assert.equal(sessions[0].uaOSVersion, 'different OS version', 'uaOSVersion is correct')
540546
assert.equal(sessions[0].uaDeviceType, 'different device type', 'uaDeviceType is correct')
541547
assert.equal(sessions[0].lastAccessTime, 42, 'lastAccessTime is correct')
548+
assert.equal(sessions[0].authAt, 1234567, 'authAt is correct')
542549

543550
// Fetch the newly verified session token
544551
return client.getThen('/sessionToken/' + user.sessionTokenId)
@@ -555,6 +562,7 @@ module.exports = function(cfg, makeServer) {
555562
assert.equal(token.uaOSVersion, 'different OS version', 'uaOSVersion was updated')
556563
assert.equal(token.uaDeviceType, 'different device type', 'uaDeviceType was updated')
557564
assert.equal(token.lastAccessTime, 42, 'lastAccessTime was updated')
565+
assert.equal(token.authAt, 1234567, 'authAt was updated')
558566

559567
// Create a device
560568
return client.putThen('/account/' + user.accountId + '/device/' + user.deviceId, user.device)

lib/db/mem.js

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,7 @@ module.exports = function (log, error) {
611611
uaDeviceType: sessionToken.uaDeviceType || null,
612612
uaFormFactor: sessionToken.uaFormFactor || null,
613613
lastAccessTime: sessionToken.lastAccessTime,
614+
authAt: sessionToken.authAt || sessionToken.createdAt,
614615
// device information
615616
deviceId: deviceInfo.id || null,
616617
deviceName: deviceInfo.name || null,
@@ -653,6 +654,7 @@ module.exports = function (log, error) {
653654
item.uaDeviceType = sessionTokens[id].uaDeviceType || null
654655
item.uaFormFactor = sessionTokens[id].uaFormFactor || null
655656
item.lastAccessTime = sessionTokens[id].lastAccessTime
657+
item.authAt = sessionTokens[id].authAt || sessionTokens[id].createdAt
656658

657659
var accountId = sessionTokens[id].uid.toString('hex')
658660
var account = accounts[accountId]
@@ -931,16 +933,15 @@ module.exports = function (log, error) {
931933
}
932934

933935
Memory.prototype.updateSessionToken = function (id, data) {
934-
var token = sessionTokens[id.toString('hex')]
936+
const hexId = id.toString('hex')
937+
const token = sessionTokens[hexId]
935938
if (! token) {
936939
return P.reject(error.notFound())
937940
}
938-
token.uaBrowser = data.uaBrowser
939-
token.uaBrowserVersion = data.uaBrowserVersion
940-
token.uaOS = data.uaOS
941-
token.uaOSVersion = data.uaOSVersion
942-
token.uaDeviceType = data.uaDeviceType
943-
token.lastAccessTime = data.lastAccessTime
941+
Object.assign(token, data)
942+
if (data.mustVerify && unverifiedTokens[hexId]) {
943+
unverifiedTokens[hexId].mustVerify = true
944+
}
944945
return P.resolve({})
945946
}
946947

@@ -1058,7 +1059,6 @@ module.exports = function (log, error) {
10581059
return P.resolve({ createdAt: timestamp })
10591060
}
10601061

1061-
10621062
Memory.prototype.createEmailBounce = function (data) {
10631063
const row = emailBounces[data.email] || (emailBounces[data.email] = [])
10641064
const bounce = extend({}, data)

lib/db/mysql.js

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -387,29 +387,30 @@ module.exports = function (log, error) {
387387

388388
// Select : sessionTokens t, accounts a, devices d, unverifiedTokens ut
389389
// Fields : t.tokenData, t.uid, t.createdAt, t.uaBrowser, t.uaBrowserVersion, t.uaOS,
390-
// t.uaOSVersion, t.uaDeviceType, t.uaFormFactor, t.lastAccessTime, a.emailVerified,
391-
// a.email, a.emailCode, a.verifierSetAt, a.locale, a.createdAt AS accountCreatedAt,
390+
// t.uaOSVersion, t.uaDeviceType, t.uaFormFactor, t.lastAccessTime, t.authAt,
391+
// a.emailVerified, a.email, a.emailCode, a.verifierSetAt, a.locale,
392+
// a.createdAt AS accountCreatedAt,
392393
// d.id AS deviceId, d.name AS deviceName, d.type AS deviceType, d.createdAt
393394
// AS deviceCreatedAt, d.callbackURL AS deviceCallbackURL, d.callbackPublicKey
394395
// AS deviceCallbackPublicKey, d.callbackAuthKey AS deviceCallbackAuthKey,
395396
// d.callbackIsExpired AS deviceCallbackIsExpired,
396397
// ut.tokenVerificationId, ut.mustVerify
397398
// Where : t.tokenId = $1 AND t.uid = a.uid AND t.tokenId = d.sessionTokenId AND
398399
// t.uid = d.uid AND t.tokenId = u.tokenId
399-
var SESSION_DEVICE = 'CALL sessionWithDevice_10(?)'
400+
var SESSION_DEVICE = 'CALL sessionWithDevice_11(?)'
400401

401402
MySql.prototype.sessionWithDevice = function (id) {
402403
return this.readFirstResult(SESSION_DEVICE, [id])
403404
}
404405

405406
// Select : sessionTokens
406407
// Fields : tokenId, uid, createdAt, uaBrowser, uaBrowserVersion,
407-
// uaOS, uaOSVersion, uaDeviceType, uaFormFactor, lastAccessTime,
408+
// uaOS, uaOSVersion, uaDeviceType, uaFormFactor, lastAccessTime, authAt,
408409
// deviceId, deviceName, deviceType, deviceCreatedAt, deviceCallbackURL,
409410
// deviceCallbackPublicKey, deviceCallbackAuthKey, deviceCallbackIsExpired
410411
// Where : t.uid = $1 AND t.tokenId = d.sessionTokenId AND
411412
// t.uid = d.uid AND t.tokenId = u.tokenId
412-
var SESSIONS = 'CALL sessions_7(?)'
413+
var SESSIONS = 'CALL sessions_8(?)'
413414

414415
MySql.prototype.sessions = function (uid) {
415416
return this.readOneFromFirstResult(SESSIONS, [uid])
@@ -418,10 +419,11 @@ module.exports = function (log, error) {
418419
// Select : sessionTokens t, accounts a
419420
// Fields : t.tokenData, t.uid, t.createdAt, t.uaBrowser, t.uaBrowserVersion,
420421
// t.uaOS, t.uaOSVersion, t.uaDeviceType, t.uaFormFactor, t.lastAccessTime,
422+
// t.authAt,
421423
// a.emailVerified, a.email, a.emailCode, a.verifierSetAt, a.locale,
422424
// a.createdAt AS accountCreatedAt
423425
// Where : t.tokenId = $1 AND t.uid = a.uid
424-
var SESSION_TOKEN = 'CALL sessionToken_7(?)'
426+
var SESSION_TOKEN = 'CALL sessionToken_8(?)'
425427

426428
MySql.prototype.sessionToken = function (id) {
427429
return this.readFirstResult(SESSION_TOKEN, [id])
@@ -430,10 +432,11 @@ module.exports = function (log, error) {
430432
// Select : sessionTokens t, accounts a, unverifiedTokens ut
431433
// Fields : t.tokenData, t.uid, t.createdAt, t.uaBrowser, t.uaBrowserVersion,
432434
// t.uaOS, t.uaOSVersion, t.uaDeviceType, t.uaFormFactor, t.lastAccessTime,
435+
// t.authAt,
433436
// a.emailVerified, a.email, a.emailCode, a.verifierSetAt, a.locale,
434437
// a.createdAt AS accountCreatedAt, ut.tokenVerificationId, ut.mustVerify
435438
// Where : t.tokenId = $1 AND t.uid = a.uid AND t.tokenId = ut.tokenId
436-
var SESSION_TOKEN_VERIFIED = 'CALL sessionTokenWithVerificationStatus_7(?)'
439+
var SESSION_TOKEN_VERIFIED = 'CALL sessionTokenWithVerificationStatus_8(?)'
437440

438441
MySql.prototype.sessionTokenWithVerificationStatus = function (tokenId) {
439442
return this.readFirstResult(SESSION_TOKEN_VERIFIED, [tokenId])
@@ -514,22 +517,26 @@ module.exports = function (log, error) {
514517
}
515518

516519
// Update : sessionTokens
517-
// Set : uaBrowser = $1, uaBrowserVersion = $2, uaOS = $3, uaOSVersion = $4,
518-
// uaDeviceType = $5, lastAccessTime = $6
519-
// Where : tokenId = $7
520-
var UPDATE_SESSION_TOKEN = 'CALL updateSessionToken_1(?, ?, ?, ?, ?, ?, ?)'
520+
// Set : uaBrowser = $2, uaBrowserVersion = $3, uaOS = $4, uaOSVersion = $5,
521+
// uaDeviceType = $6, uaFormFactor = $7, lastAccessTime = $8,
522+
// authAt = $9, mustVerify = $10
523+
// Where : tokenId = $1
524+
var UPDATE_SESSION_TOKEN = 'CALL updateSessionToken_2(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'
521525

522526
MySql.prototype.updateSessionToken = function (tokenId, token) {
523527
return this.write(
524528
UPDATE_SESSION_TOKEN,
525529
[
530+
tokenId,
526531
token.uaBrowser,
527532
token.uaBrowserVersion,
528533
token.uaOS,
529534
token.uaOSVersion,
530535
token.uaDeviceType,
536+
token.uaFormFactor,
531537
token.lastAccessTime,
532-
tokenId
538+
token.authAt,
539+
token.mustVerify
533540
]
534541
)
535542
}
@@ -803,7 +810,6 @@ module.exports = function (log, error) {
803810
)
804811
}
805812

806-
807813
// USER EMAILS
808814
// Insert : emails
809815
// Values : normalizedEmail = $1, email = $2, uid = $3, emailCode = $4, isVerified = $5, verifiedAt = $7, createdAt = $8

lib/db/patch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44

55
// The expected patch level of the database. Update if you add a new
66
// patch in the ./schema/ directory.
7-
module.exports.level = 71
7+
module.exports.level = 72

0 commit comments

Comments
 (0)