implemented new /auth/start /session/create

This commit is contained in:
Danny Coates 2013-08-06 12:44:26 -07:00
Родитель 6b8296a8f8
Коммит e9a35fac94
18 изменённых файлов: 421 добавлений и 304 удалений

Просмотреть файл

@ -190,32 +190,19 @@ ClientApi.prototype.getRandomBytes = function () {
)
}
ClientApi.prototype.passwordChangeAuthStart = function (sessionTokenHex) {
return tokens.SessionToken.fromHex(sessionTokenHex)
ClientApi.prototype.passwordChangeStart = function (authTokenHex) {
return tokens.AuthToken.fromHex(authTokenHex)
.then(
function (token) {
return doRequest(
'POST',
this.origin + '/password/change/auth/start',
this.origin + '/password/change/start',
token
)
}.bind(this)
)
}
ClientApi.prototype.passwordChangeAuthFinish = function (srpToken, A, M) {
return doRequest(
'POST',
this.origin + '/password/change/auth/finish',
null,
{
srpToken: srpToken,
A: A,
M: M
}
)
}
ClientApi.prototype.passwordForgotSendCode = function (email) {
return doRequest(
'POST',
@ -239,10 +226,10 @@ ClientApi.prototype.passwordForgotVerifyCode = function (forgotPasswordToken, co
)
}
ClientApi.prototype.sessionAuthStart = function (email) {
ClientApi.prototype.authStart = function (email) {
return doRequest(
'POST',
this.origin + '/session/auth/start',
this.origin + '/auth/start',
null,
{
email: email
@ -250,10 +237,10 @@ ClientApi.prototype.sessionAuthStart = function (email) {
)
}
ClientApi.prototype.sessionAuthFinish = function (srpToken, A, M) {
ClientApi.prototype.authFinish = function (srpToken, A, M) {
return doRequest(
'POST',
this.origin + '/session/auth/finish',
this.origin + '/auth/finish',
null,
{
srpToken: srpToken,
@ -263,13 +250,13 @@ ClientApi.prototype.sessionAuthFinish = function (srpToken, A, M) {
)
}
ClientApi.prototype.sessionStatus = function (sessionTokenHex) {
return tokens.SessionToken.fromHex(sessionTokenHex)
ClientApi.prototype.sessionCreate = function (authTokenHex) {
return tokens.AuthToken.fromHex(authTokenHex)
.then(
function (token) {
return doRequest(
'GET',
this.origin + '/session/status',
'POST',
this.origin + '/session/create',
token
)
}.bind(this)

Просмотреть файл

@ -16,6 +16,7 @@ function Client(origin) {
this.passwordSalt = null
this.srp = null
this.email = null
this.authToken = null
this.sessionToken = null
this.accountResetToken = null
this.keyFetchToken = null
@ -27,12 +28,12 @@ Client.Api = ClientApi
function getAMK(srpSession, email, password) {
var a = crypto.randomBytes(32)
var g = srp.params[srpSession.srp.N_bits].g
var N = srp.params[srpSession.srp.N_bits].N
var g = srp.params[2048].g
var N = srp.params[2048].N
var A = srp.getA(g, a, N)
var B = Buffer(srpSession.srp.B, 'hex')
var S = srp.client_getS(
Buffer(srpSession.srp.s, 'hex'),
Buffer(srpSession.srp.salt, 'hex'),
Buffer(email),
Buffer(password),
N,
@ -134,32 +135,44 @@ Client.prototype.stringify = function () {
Client.prototype.login = function (callback) {
var K = null
var p = this.api.sessionAuthStart(this.email)
var p = this.api.authStart(this.email)
.then(
function (srpSession) {
var x = getAMK(srpSession, this.email, this.password)
K = x.K
return this.api.sessionAuthFinish(x.srpToken, x.A, x.M)
return this.api.authFinish(x.srpToken, x.A, x.M)
}.bind(this)
)
.then(
function (json) {
return AuthBundle.create(K, 'session/auth')
return AuthBundle.create(K, 'auth/finish')
.then(
function (b) {
var tokens = b.unbundle(json.bundle)
return {
keyFetchToken: tokens.keyFetchToken,
sessionToken: tokens.otherToken
}
return b.unbundle(json.bundle)
}
)
}.bind(this)
)
.then(
function (authToken) {
this.authToken = authToken
return this.api.sessionCreate(this.authToken)
}.bind(this)
)
.then (
function (json) {
return tokens.AuthToken.fromHex(this.authToken)
.then(
function (t) {
return t.unbundle(json.bundle)
}
)
}.bind(this)
)
.then(
function (tokens) {
this.sessionToken = tokens.sessionToken
this.keyFetchToken = tokens.keyFetchToken
this.sessionToken = tokens.sessionToken
return tokens
}.bind(this)
)

Просмотреть файл

@ -7,6 +7,7 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
var domain = config.domain
var SessionToken = tokens.SessionToken
var AccountResetToken = tokens.AccountResetToken
var AuthToken = tokens.AuthToken
function Account() {
this.uid = null
@ -17,6 +18,7 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
this.srp = null
this.passwordStretching = null
// references
this.authTokenId = null
this.resetTokenId = null
this.sessionTokenIds = null
this.recoveryMethodIds = null
@ -33,6 +35,7 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
a.kA = object.kA
a.wrapKb = object.wrapKb
a.passwordStretching = object.passwordStretching
a.authTokenId = object.authTokenId
a.resetTokenId = object.resetTokenId
a.sessionTokenIds = object.sessionTokenIds || {}
a.recoveryMethodIds = object.recoveryMethodIds || {}
@ -165,6 +168,19 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
return set()
}
Account.prototype.setAuthToken = function (token) {
var set = function () {
this.authTokenId = token.id
return this.save()
}.bind(this)
if (this.authTokenId !== null) {
return AuthToken
.del(this.authTokenId)
.then(set)
}
return set()
}
Account.prototype.deleteSessionToken = function (id) {
delete this.sessionTokenIds[id]
return SessionToken.del(id)
@ -241,6 +257,7 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
.then(this.deleteAllRecoveryMethods.bind(this))
.then(deleteRecord.bind(null, this.uid))
.then(deleteIndex.bind(null, this.email))
.then(function () { return {} })
}
return Account

Просмотреть файл

@ -6,63 +6,34 @@ module.exports = function (inherits, Bundle, Account, tokens) {
function AuthBundle() {
Bundle.call(this)
this.keyFetchToken = null
this.authToken = null
this.otherToken = null
}
inherits(AuthBundle, Bundle)
AuthBundle.create = function (K, type) {
return Bundle
.hkdf(K, type, null, 3 * 32)
.hkdf(K, type, null, 2 * 32)
.then(
function (key) {
var b = new AuthBundle()
b.hmacKey = key.slice(0, 32).toString('hex')
b.xorKey = key.slice(32, 96).toString('hex')
b.xorKey = key.slice(32, 64).toString('hex')
return b
}
)
}
AuthBundle.login = function (K, uid) {
return AuthBundle.create(K, 'session/auth')
return AuthBundle.create(K, 'auth/finish')
.then(
function (b) {
return tokens.KeyFetchToken
.create(uid)
.then(function (t) { b.keyFetchToken = t })
.then(tokens.SessionToken.create.bind(null, uid))
.then(function (t) { b.otherToken = t })
return tokens.AuthToken.create(uid)
.then(function (t) { b.authToken = t })
.then(Account.get.bind(null, uid))
.then(
function (a) {
return a.addSessionToken(b.otherToken)
}
)
.then(
function () {
return {
bundle: b.bundle()
}
}
)
}
)
}
AuthBundle.passwordChange = function (K, uid) {
return AuthBundle.create(K, 'password/change')
.then(
function (b) {
return tokens.KeyFetchToken
.create(uid)
.then(function (t) { b.keyFetchToken = t })
.then(tokens.AccountResetToken.create.bind(null, uid))
.then(function (t) { b.otherToken = t })
.then(Account.get.bind(null, uid))
.then(
function (a) {
return a.setResetToken(b.otherToken)
return a.setAuthToken(b.authToken)
}
)
.then(
@ -78,20 +49,17 @@ module.exports = function (inherits, Bundle, Account, tokens) {
AuthBundle.prototype.unbundle = function (hex) {
var bundle = Buffer(hex, 'hex')
var ciphertext = bundle.slice(0, 64)
var hmac = bundle.slice(64, 96)
var ciphertext = bundle.slice(0, 32)
var hmac = bundle.slice(32, 64)
if (this.hmac(ciphertext).toString('hex') !== hmac.toString('hex')) {
throw new Error('Corrupt Message')
}
var plaintext = this.xor(ciphertext)
return {
keyFetchToken: plaintext.slice(0, 32).toString('hex'),
otherToken: plaintext.slice(32, 64).toString('hex')
}
return plaintext.slice(0, 32).toString('hex')
}
AuthBundle.prototype.bundle = function () {
return this.bundleHexStrings([this.keyFetchToken.data, this.otherToken.data])
return this.bundleHexStrings([this.authToken.data])
}
return AuthBundle

104
models/auth_token.js Normal file
Просмотреть файл

@ -0,0 +1,104 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (inherits, Token, db) {
function AuthToken() {
Token.call(this)
}
inherits(AuthToken, Token)
AuthToken.hydrate = function (object) {
return Token.fill(new AuthToken(), object)
}
AuthToken.create = function (uid) {
return Token
.randomTokenData('session/create', 5 * 32)
.then(
function (data) {
var key = data[1]
var t = new AuthToken()
t.uid = uid
t.data = data[0].toString('hex')
t.id = key.slice(0, 32).toString('hex')
t.key = key.slice(32, 64).toString('hex')
t.hmacKey = key.slice(64, 96).toString('hex')
t.xorKey = key.slice(96, 160).toString('hex')
return t.save()
}
)
}
AuthToken.getCredentials = function (id, cb) {
AuthToken.get(id)
.done(
function (token) {
cb(null, token)
},
cb
)
}
AuthToken.fromHex = function (string) {
return Token.
tokenDataFromBytes(
'session/create',
5 * 32,
Buffer(string, 'hex')
)
.then(
function (data) {
var key = data[1]
var t = new AuthToken()
t.data = data[0].toString('hex')
t.id = key.slice(0, 32).toString('hex')
t.key = key.slice(32, 64).toString('hex')
t.hmacKey = key.slice(64, 96).toString('hex')
t.xorKey = key.slice(96, 160).toString('hex')
return t
}
)
}
AuthToken.get = function (id) {
return db
.get(id + '/auth')
.then(AuthToken.hydrate)
}
AuthToken.del = function (id) {
return db.delete(id + '/auth')
}
AuthToken.prototype.save = function () {
var self = this
return db.set(this.id + '/auth', this).then(function () { return self })
}
AuthToken.prototype.del = function () {
return AuthToken.del(this.id)
}
AuthToken.prototype.bundle = function (keyFetchToken, sessionToken) {
return this.bundleHexStrings([keyFetchToken.data, sessionToken.data])
}
AuthToken.prototype.unbundle = function (bundle) {
var buffer = Buffer(bundle, 'hex')
var ciphertext = buffer.slice(0, 64)
var hmac = buffer.slice(64, 96).toString('hex')
if(this.hmac(ciphertext).toString('hex') !== hmac) {
throw new Error('Unmatching HMAC')
}
var plaintext = this.xor(ciphertext)
return {
// TODO: maybe parse the tokens here
keyFetchToken: plaintext.slice(0, 32).toString('hex'),
sessionToken: plaintext.slice(32, 64).toString('hex')
}
}
return AuthToken
}

Просмотреть файл

@ -32,10 +32,16 @@ module.exports = function (config, dbs, mailer) {
Token,
dbs.store
)
var AuthToken = require('./auth_token')(
inherits,
Token,
dbs.cache
)
var tokens = {
AccountResetToken: AccountResetToken,
KeyFetchToken: KeyFetchToken,
SessionToken: SessionToken
SessionToken: SessionToken,
AuthToken: AuthToken
}
var RecoveryMethod = require('./recovery_method')(

Просмотреть файл

@ -16,7 +16,7 @@ module.exports = function (P, uuid, srp, db, error) {
this.b = null
this.B = null
this.K = null
this.type = null // 'login' | 'passwordChange'
this.passwordStretching = null
}
function srpGenKey() {
@ -29,7 +29,7 @@ module.exports = function (P, uuid, srp, db, error) {
return d.promise
}
SrpSession.create = function (type, account) {
SrpSession.create = function (account) {
var session = null
if (!account) { throw error.unknownAccount() }
return srpGenKey()
@ -44,7 +44,7 @@ module.exports = function (P, uuid, srp, db, error) {
session.v = Buffer(account.srp.verifier, 'hex')
session.b = b
session.B = srp.getB(session.v, session.g, b, session.N, alg)
session.type = type
session.passwordStretching = account.passwordStretching
return session.save()
}
)
@ -54,8 +54,8 @@ module.exports = function (P, uuid, srp, db, error) {
return db.delete(id + '/srp')
}
SrpSession.start = function (type, account) {
return SrpSession.create(type, account)
SrpSession.start = function (account) {
return SrpSession.create(account)
.then(
function (session) {
return session.clientData()
@ -105,7 +105,6 @@ module.exports = function (P, uuid, srp, db, error) {
s.v = Buffer(object.v, 'hex')
s.b = Buffer(object.b, 'hex')
s.B = Buffer(object.B, 'hex')
s.type = object.type
return s
}
@ -118,13 +117,10 @@ module.exports = function (P, uuid, srp, db, error) {
SrpSession.prototype.clientData = function () {
return {
srpToken: this.id,
stretch: {
salt: 'FEED'
},
passwordStretching: this.passwordStretching,
srp: {
N_bits: 2048,
alg: 'sha256',
s: this.s,
type: 'SRP-6a/SHA256/2048/v1',
salt: this.s,
B: this.B.toString('hex')
}
}
@ -139,7 +135,7 @@ module.exports = function (P, uuid, srp, db, error) {
var A = srp.getA(g, a, N)
var B = Buffer(session.srp.B, 'hex')
var S = srp.client_getS(
Buffer(session.srp.s, 'hex'),
Buffer(session.srp.salt, 'hex'),
Buffer(email),
Buffer(password),
N,
@ -150,7 +146,7 @@ module.exports = function (P, uuid, srp, db, error) {
)
var M = srp.getM(A, B, S, N)
var K = srp.getK(S, N, session.srp.alg)
var K = srp.getK(S, N, 'sha256')
return {
A: A.toString('hex'),
M: M.toString('hex'),
@ -169,8 +165,7 @@ module.exports = function (P, uuid, srp, db, error) {
s: this.s,
v: this.v.toString('hex'),
b: this.b.toString('hex'),
B: this.B.toString('hex'),
type: this.type
B: this.B.toString('hex')
}).then(function () { return self })
}

Просмотреть файл

@ -304,6 +304,28 @@ module.exports = function (crypto, uuid, isA, error, Account, RecoveryMethod) {
.done(reply, reply)
},
}
},
{
method: 'POST',
path: '/account/destroy',
config: {
auth: {
strategy: 'authToken'
},
tags: ["account"],
handler: function accountDestroy(request) {
var reply = request.reply.bind(request)
var authToken = request.auth.credentials
authToken.del()
.then(
function () {
return Account.del(authToken.uid)
}
)
.done(reply, reply)
}
}
}
]

81
routes/auth.js Normal file
Просмотреть файл

@ -0,0 +1,81 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (isA, Account, SrpSession, AuthBundle) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
var routes = [
{
method: 'POST',
path: '/auth/start',
config: {
description:
"Begins an SRP login for the supplied email address, " +
"returning the temporary srpToken and parameters for " +
"key stretching and the SRP protocol for the client.",
tags: ["srp", "account"],
handler: function (request) {
var reply = request.reply.bind(request)
Account.getByEmail(request.payload.email)
.then(SrpSession.start.bind(null))
.done(reply, reply)
},
validate: {
payload: {
email: isA.String().email().required()
},
response: {
schema: {
srpToken: isA.String().required(),
passwordStretching: isA.Object(),
srp: isA.Object({
type: isA.String().required(),
salt: isA.String().regex(HEX_STRING), // salt
B: isA.String().regex(HEX_STRING) // server's public key value
})
}
}
}
}
},
{
method: 'POST',
path: '/auth/finish',
config: {
description:
"Finishes the SRP dance, with the client providing " +
"proof-of-knownledge of the password and receiving " +
"the bundle encrypted with the authToken.",
tags: ["srp", "session"],
handler: function (request) {
var reply = request.reply.bind(request)
SrpSession
.finish(request.payload.srpToken, request.payload.A, request.payload.M)
.then(
function (srpSession) {
return AuthBundle.login(srpSession.K, srpSession.uid)
}
)
.done(reply, reply)
},
validate: {
payload: {
srpToken: isA.String().required(),
A: isA.String().regex(HEX_STRING).required(),
M: isA.String().regex(HEX_STRING).required()
},
response: {
schema: {
bundle: isA.String().regex(HEX_STRING).required()
}
}
}
}
},
]
return routes
}

Просмотреть файл

@ -16,16 +16,16 @@ module.exports = function (
signer,
models
) {
var srp = require('./srp')(models.SrpSession, models.AuthBundle)
var auth = require('./auth')(isA, models.Account, models.SrpSession, models.AuthBundle)
var defaults = require('./defaults')(P, models.dbs)
var idp = require('./idp')(crypto, error, isA, serverPublicKey)
var account = require('./account')(crypto, uuid, isA, error, models.Account, models.RecoveryMethod)
var password = require('./password')(isA, error, srp, models.Account)
var session = require('./session')(srp, isA, error, models.Account)
var password = require('./password')(isA, error, models.Account)
var session = require('./session')(isA, error, models.Account, models.tokens)
var sign = require('./sign')(isA, error, signer, models.Account)
var routes = defaults.concat(
auth,
idp,
account,
password,

Просмотреть файл

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (isA, error, srp, Account) {
module.exports = function (isA, error, Account) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
@ -11,69 +11,6 @@ module.exports = function (isA, error, srp, Account) {
}
var routes = [
{
method: 'POST',
path: '/password/change/auth/start',
config: {
description:
"Begins an SRP login for the authenticated user, " +
"returning the temporary sessionId and parameters for " +
"key stretching and the SRP protocol for the client.",
tags: ["srp", "password"],
handler: function (request) {
Account
.get(request.auth.credentials.uid)
.done(
function (account) {
return srp.start('passwordChange', account, request)
}
)
},
auth: {
strategy: 'sessionToken'
},
validate: {
response: {
schema: {
srpToken: isA.String().required(),
stretch: isA.Object({
salt: isA.String()
}),
srp: isA.Object({
N_bits: isA.Number().valid(2048), // number of bits for prime
alg: isA.String().valid('sha256'), // hash algorithm (sha256)
s: isA.String().regex(HEX_STRING), // salt
B: isA.String().regex(HEX_STRING) // server's public key value
})
}
}
}
}
},
{
method: 'POST',
path: '/password/change/auth/finish',
handler: srp.finish,
config: {
description:
"Finishes the SRP dance, with the client providing " +
"proof-of-knownledge of the password and receiving " +
"the bundle encrypted with the shared key.",
tags: ["srp", "password"],
validate: {
payload: {
srpToken: isA.String().required(),
A: isA.String().regex(HEX_STRING).required(),
M: isA.String().regex(HEX_STRING).required()
},
response: {
schema: {
bundle: isA.String().regex(HEX_STRING).required()
}
}
}
}
},
{
method: 'POST',
path: '/password/forgot/send_code',

Просмотреть файл

@ -2,86 +2,48 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
module.exports = function (srp, isA, error, Account) {
module.exports = function (isA, error, Account, tokens) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
var routes = [
{
method: 'POST',
path: '/session/auth/start',
path: '/session/create',
config: {
description:
"Begins an SRP login for the supplied email address, " +
"returning the temporary sessionId and parameters for " +
"key stretching and the SRP protocol for the client.",
tags: ["srp", "account"],
handler: function (request) {
Account
.getByEmail(request.payload.email)
.done(
function (account) {
return srp.start('login', account, request)
}
)
},
validate: {
payload: {
email: isA.String().email().required()
},
response: {
schema: {
srpToken: isA.String().required(),
stretch: isA.Object({
salt: isA.String()
}),
srp: isA.Object({
N_bits: isA.Number().valid(2048), // number of bits for prime
alg: isA.String().valid('sha256'), // hash algorithm (sha256)
s: isA.String().regex(HEX_STRING), // salt
B: isA.String().regex(HEX_STRING) // server's public key value
})
}
}
}
}
},
{
method: 'POST',
path: '/session/auth/finish',
handler: srp.finish,
config: {
description:
"Finishes the SRP dance, with the client providing " +
"proof-of-knownledge of the password and receiving " +
"the bundle encrypted with the shared key.",
tags: ["srp", "session"],
validate: {
payload: {
srpToken: isA.String().required(),
A: isA.String().regex(HEX_STRING).required(),
M: isA.String().regex(HEX_STRING).required()
},
response: {
schema: {
bundle: isA.String().regex(HEX_STRING).required()
}
}
}
}
},
{
method: 'GET',
path: '/session/status',
config: {
description: "Check whether a session is still valid.",
description: "Creates a new session",
tags: ["session"],
auth: {
strategy: 'sessionToken'
strategy: 'authToken'
},
handler: function (request) {
// hapi will take care of the case where the token isn't valid
request.reply({})
var reply = request.reply.bind(request)
var authToken = request.auth.credentials
var keyFetchToken = null
var sessionToken = null
authToken.del()
.then(
function () {
return tokens.KeyFetchToken.create(authToken.uid)
}
)
.then(function (t) { keyFetchToken = t })
.then(tokens.SessionToken.create.bind(null, authToken.uid))
.then(function (t) { sessionToken = t })
.then(Account.get.bind(null, authToken.uid))
.then(
function (account) {
return account.addSessionToken(sessionToken)
}
)
.then(
function () {
return {
bundle: authToken.bundle(keyFetchToken, sessionToken)
}
}
)
.done(reply, reply)
}
}
},

Просмотреть файл

@ -6,8 +6,7 @@ module.exports = function (SrpSession, AuthBundle) {
function srpStart(type, account, request) {
var reply = request.reply.bind(request)
SrpSession
.start(type, account)
SrpSession.start(account)
.done(reply, reply)
}
@ -17,7 +16,7 @@ module.exports = function (SrpSession, AuthBundle) {
.finish(request.payload.srpToken, request.payload.A, request.payload.M)
.then(
function (srpSession) {
return AuthBundle[srpSession.type](srpSession.K, srpSession.uid)
return AuthBundle.login(srpSession.K, srpSession.uid)
}
)
.done(reply, reply)

Просмотреть файл

@ -22,6 +22,10 @@ module.exports = function (path, Hapi, toobusy) {
accountResetToken: {
scheme: 'hawk',
getCredentialsFunc: tokens.AccountResetToken.getCredentials
},
authToken: {
scheme: 'hawk',
getCredentialsFunc: tokens.AuthToken.getCredentials
}
},
cors: true,

38
test/auth_token_tests.js Normal file
Просмотреть файл

@ -0,0 +1,38 @@
var test = require('tap').test
var crypto = require('crypto')
var P = require('p-promise')
var config = require('../config').root()
var dbs = require('../kv')(config)
var mailer = {
sendCode: function () { return P(null) }
}
var models = require('../models')(config, dbs, mailer)
var AuthToken = models.tokens.AuthToken
test(
'bundle / unbundle works',
function (t) {
function end() { t.end() }
AuthToken.create('xxx')
.then(
function (x) {
var keyFetchTokenHex = crypto.randomBytes(32).toString('hex')
var sessionTokenHex = crypto.randomBytes(32).toString('hex')
var b = x.bundle(keyFetchTokenHex, sessionTokenHex)
var ub = x.unbundle(b)
t.equal(ub.keyFetchTokenHex, keyFetchTokenHex)
t.equal(ub.sessionTokenHex, sessionTokenHex)
return x
}
)
.then(
function (x) {
return x.del()
}
)
.done(end, end)
}
)

Просмотреть файл

@ -261,7 +261,10 @@ var RToken = require('../models/token')(inherits, RBundle)
var KeyFetchToken = require('../models/key_fetch_token')(inherits, KToken, dbs.store)
var AccountResetToken = require('../models/account_reset_token')(inherits, RToken, crypto, dbs.store)
var SessionToken = require('../models/session_token')(inherits, SToken, dbs.store)
var AuthToken = require('../models/auth_token')(inherits, SToken, dbs.store)
var tokens = {
AuthToken: AuthToken,
KeyFetchToken: KeyFetchToken,
AccountResetToken: AccountResetToken,
SessionToken: SessionToken
@ -277,8 +280,8 @@ FakeAccount.prototype.addSessionToken = function (t) {
this.sessionTokenIds[t.id] = true
return P(null)
}
FakeAccount.prototype.setResetToken = function (t) {
this.resetTokenId = t.id
FakeAccount.prototype.setAuthToken = function (t) {
this.authTokenId = t.id
return P(null)
}
@ -334,72 +337,53 @@ test(
}
)
test(
'/session/auth/finish',
function (t) {
account = new FakeAccount()
AuthBundle.login(sessionAuth.K, 'xxx')
.done(
function (authBundle) {
var b = authBundle.bundle
t.equal(b, sessionAuth.ciphertext + sessionAuth.hmac)
t.end()
},
function (err) {
t.fail(err)
t.end()
}
)
}
)
// test(
// '/session/auth/finish',
// function (t) {
// account = new FakeAccount()
// AuthBundle.login(sessionAuth.K, 'xxx')
// .done(
// function (authBundle) {
// var b = authBundle.bundle
// t.equal(b, sessionAuth.ciphertext + sessionAuth.hmac)
// t.end()
// },
// function (err) {
// t.fail(err)
// t.end()
// }
// )
// }
// )
// test(
// '/password/change/auth/finish',
// function (t) {
// account = new FakeAccount()
// AuthBundle.passwordChange(sessionAuth.K, 'xxx')
// .done(
// function (changePasswordBundle) {
// var b = changePasswordBundle.bundle
// t.equal(b, passwordChange.ciphertext + passwordChange.hmac)
// t.end()
// },
// function (err) {
// t.fail(err)
// t.end()
// }
// )
// }
// )
test(
'/password/change/auth/finish',
'login sets authToken on account',
function (t) {
account = new FakeAccount()
AuthBundle.passwordChange(sessionAuth.K, 'xxx')
.done(
function (changePasswordBundle) {
var b = changePasswordBundle.bundle
t.equal(b, passwordChange.ciphertext + passwordChange.hmac)
t.end()
},
function (err) {
t.fail(err)
t.end()
}
)
}
)
test(
'login adds sessionToken to account',
function (t) {
account = new FakeAccount()
t.equal(Object.keys(account.sessionTokenIds).length, 0)
t.equal(!!account.authTokenId, false)
AuthBundle.login(sessionAuth.K, 'xxx')
.done(
function () {
t.equal(Object.keys(account.sessionTokenIds).length, 1)
t.end()
},
function (err) {
t.fail(err)
t.end()
}
)
}
)
test(
'changePassword sets resetToken on account',
function (t) {
account = new FakeAccount()
t.equal(!!account.resetTokenId, false)
AuthBundle.passwordChange(sessionAuth.K, 'xxx')
.done(
function () {
t.equal(!!account.resetTokenId, true)
t.equal(!!account.authTokenId, true)
t.end()
},
function (err) {

Просмотреть файл

@ -22,6 +22,9 @@ var server = cp.spawn(
}
)
server.stdout.on('data', process.stdout.write.bind(process.stdout))
server.stderr.on('data', process.stderr.write.bind(process.stderr))
function main() {
test(
'Create account flow',
@ -57,7 +60,7 @@ function main() {
},
function (err) {
server.kill('SIGINT')
t.fail(err.message)
t.fail(err.message || err.error)
t.end()
}
)
@ -78,6 +81,7 @@ function waitLoop() {
.done(
main,
function (err) {
console.log('waiting...')
setTimeout(waitLoop, 100)
}
)

Просмотреть файл

@ -41,14 +41,11 @@ Account.create(alice)
test(
'create login session works',
function (t) {
SrpSession
.create('login', a)
SrpSession.create(a)
.done(
function (s) {
t.equal(s.uid, a.uid)
t.equal(s.s, a.srp.salt)
t.equal(s.type, 'login')
t.end()
}
)
@ -60,8 +57,7 @@ Account.create(alice)
function (t) {
var session = null
var K = null
SrpSession
.create('login', a)
SrpSession.create(a)
.then(
function (s) {
session = s