updated /account/create to next api
This commit is contained in:
Родитель
970023966f
Коммит
df0ac8e8e9
|
@ -64,16 +64,19 @@ function doRequest(method, url, token, payload) {
|
|||
* {}
|
||||
*
|
||||
*/
|
||||
ClientApi.prototype.accountCreate = function (email, verifier, salt, params) {
|
||||
ClientApi.prototype.accountCreate = function (email, verifier, salt, passwordStretching) {
|
||||
return doRequest(
|
||||
'POST',
|
||||
this.origin + '/account/create',
|
||||
null,
|
||||
{
|
||||
email: email,
|
||||
verifier: verifier,
|
||||
salt: salt,
|
||||
params: params
|
||||
srp: {
|
||||
type: 'SRP-6a/SHA256/2048/v1',
|
||||
verifier: verifier,
|
||||
salt: salt
|
||||
},
|
||||
passwordStretching: passwordStretching
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -14,7 +14,8 @@ var AuthBundle = models.AuthBundle
|
|||
function Client(origin) {
|
||||
this.api = new ClientApi(origin)
|
||||
this.algorithm = 'sha256'
|
||||
this.salt = null
|
||||
this.srpSalt = null
|
||||
this.passwordSalt = null
|
||||
this.email = null
|
||||
this.verifier = null
|
||||
this.sessionToken = null
|
||||
|
@ -67,10 +68,12 @@ function verifier(salt, email, password, algorithm) {
|
|||
|
||||
Client.create = function (origin, email, password, callback) {
|
||||
var c = new Client(origin)
|
||||
// TODO: password stretching
|
||||
c.email = email
|
||||
c.password = password
|
||||
c.salt = crypto.randomBytes(32).toString('hex')
|
||||
c.verifier = verifier(c.salt, c.email, c.password, c.algorithm)
|
||||
c.srpSalt = crypto.randomBytes(32).toString('hex')
|
||||
c.verifier = verifier(c.srpSalt, c.email, c.password, c.algorithm)
|
||||
c.passwordSalt = crypto.randomBytes(32).toString('hex')
|
||||
var p = c.create()
|
||||
if (callback) {
|
||||
p.done(callback.bind(null, null), callback)
|
||||
|
@ -83,11 +86,11 @@ Client.create = function (origin, email, password, callback) {
|
|||
Client.parse = function (string) {
|
||||
var object = JSON.parse(string)
|
||||
var client = new Client(object.api.origin)
|
||||
client.salt = object.salt
|
||||
client.email = object.email
|
||||
client.password = object.password
|
||||
client.salt = object.salt
|
||||
client.verifier = object.verifier
|
||||
client.srp = object.srp
|
||||
c.passwordSalt = object.passwordSalt
|
||||
client.passwordStretching = object.passwordStretching
|
||||
client.sessionToken = object.sessionToken
|
||||
client.accountResetToken = object.accountResetToken
|
||||
client.keyFetchToken = object.keyFetchToken
|
||||
|
@ -101,16 +104,15 @@ Client.prototype.create = function (callback) {
|
|||
var p = this.api.accountCreate(
|
||||
this.email,
|
||||
this.verifier,
|
||||
this.salt,
|
||||
this.srpSalt,
|
||||
{
|
||||
srp: {
|
||||
alg: this.algorithm,
|
||||
N_bits: 2048
|
||||
},
|
||||
stretch: {
|
||||
salt: 'DEAD',
|
||||
rounds: 0
|
||||
}
|
||||
type: 'PBKDF2/scrypt/PBKDF2/v1',
|
||||
PBKDF2_rounds_1: 20000,
|
||||
scrypt_N: 65536,
|
||||
scrypt_r: 8,
|
||||
scrypt_p: 1,
|
||||
PBKDF2_rounds_2: 20000,
|
||||
salt: this.passwordSalt
|
||||
}
|
||||
)
|
||||
.then(
|
||||
|
@ -232,9 +234,9 @@ Client.prototype.changePassword = function (newPassword, callback) {
|
|||
)
|
||||
.then(
|
||||
function (token) {
|
||||
this.salt = crypto.randomBytes(32).toString('hex')
|
||||
this.srpSalt = crypto.randomBytes(32).toString('hex')
|
||||
this.password = newPassword
|
||||
this.verifier = verifier(this.salt, this.email, newPassword, this.algorithm)
|
||||
this.verifier = verifier(this.srpSalt, this.email, newPassword, this.algorithm)
|
||||
var bundle = token.bundle(this.wrapKb, this.verifier)
|
||||
var params = this.params
|
||||
return this.api.accountReset(
|
||||
|
|
49
docs/api.md
49
docs/api.md
|
@ -78,18 +78,17 @@ Creates a user account. The client provides the email address with which this ac
|
|||
___Parameters___
|
||||
|
||||
* email - the primary email for this account
|
||||
* mainKDFSalt - salt for main-KDF
|
||||
* srpSalt - SRP salt
|
||||
* srpVerifier - the derived SRP verifier
|
||||
* params
|
||||
* srpParametersVersion: "1" (meaning SRP-6a, SHA256, with a specific 2048-bit group)
|
||||
* stretch
|
||||
* keyStretchingVersion: "1" (meaning PBKDF2/scrypt/PBKDF2)
|
||||
* PBKDF2_rounds_1: 20000
|
||||
* scrypt_N: 65536
|
||||
* scrypt_r: 8
|
||||
* scrypt_p: 1
|
||||
* PBKDF2_rounds_2: 20000
|
||||
* srp
|
||||
* type - "SRP-6a/SHA256/2048/v1"
|
||||
* verifer - the derived SRP verifier
|
||||
* salt - SRP salt
|
||||
* passwordStretching
|
||||
* type: "PBKDF2/scrypt/PBKDF2/v1"
|
||||
* PBKDF2_rounds_1: 20000
|
||||
* scrypt_N: 65536
|
||||
* scrypt_r: 8
|
||||
* scrypt_p: 1
|
||||
* PBKDF2_rounds_2: 20000
|
||||
|
||||
### Request
|
||||
```sh
|
||||
|
@ -99,19 +98,19 @@ curl -v \
|
|||
http://idp.profileinthecloud.net/account/create \
|
||||
-d '{
|
||||
"email": "me2@example.com",
|
||||
"mainKDFSalt: "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab",
|
||||
"srpSalt: "f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4",
|
||||
"srpVerifier": "7597c55064c73bf1b2735878cb8711c289fc8f1cfb3d633a4593b36a8c51dbd68b27f649949de27d1dcccf7ece1e1a42c5c6bdc3d209cf13a3813d333bfcadd2641a9a3e2eb4289788ed8510cc8f2f1061789d58aef38b9d21b81831413f55473f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4e0bda74fcaa2347be4664f553d332df8166278c0e2f8663aa9238a2429631f7afd11622e193747b57975c51bbb69bb11f60c1a5ba449d3119e70d1ec580212151f79b26e73a57dba313376f0ba7a2afc232146a3b1d68b2d0afc35ebb8699cb10b3a3f8e0d51cefc7ac29212b238fb7a87f2f61edc9cbff103e386f778925fe",
|
||||
"params": {
|
||||
"srpParametersVersion": "1",
|
||||
"stretch": {
|
||||
"keyStretchingVersion": "1",
|
||||
"PBKDF2_rounds_1": 20000,
|
||||
"scrypt_N": 65536,
|
||||
"scrypt_r": 8,
|
||||
"scrypt_p": 1,
|
||||
"PBKDF2_rounds_2": 20000
|
||||
}
|
||||
"srp": {
|
||||
"type": "SRP-6a/SHA256/2048/v1",
|
||||
"verifier": "7597c55064c73bf1b2735878cb8711c289fc8f1cfb3d633a4593b36a8c51dbd68b27f649949de27d1dcccf7ece1e1a42c5c6bdc3d209cf13a3813d333bfcadd2641a9a3e2eb4289788ed8510cc8f2f1061789d58aef38b9d21b81831413f55473f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4e0bda74fcaa2347be4664f553d332df8166278c0e2f8663aa9238a2429631f7afd11622e193747b57975c51bbb69bb11f60c1a5ba449d3119e70d1ec580212151f79b26e73a57dba313376f0ba7a2afc232146a3b1d68b2d0afc35ebb8699cb10b3a3f8e0d51cefc7ac29212b238fb7a87f2f61edc9cbff103e386f778925fe",
|
||||
salt: "f9fae9253549b2428a403d6fa51e6fb43d2f8a302e132cf902ffade52c02e6a4"
|
||||
},
|
||||
"passwordStretching": {
|
||||
"type": "PBKDF2/scrypt/PBKDF2/v1",
|
||||
"PBKDF2_rounds_1": 20000,
|
||||
"scrypt_N": 65536,
|
||||
"scrypt_r": 8,
|
||||
"scrypt_p": 1,
|
||||
"PBKDF2_rounds_2": 20000,
|
||||
"salt": "996bc6b1aa63cd69856a2ec81cbf19d5c8a604713362df9ee15c2bf07128efab"
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
|
|
@ -12,11 +12,10 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
|
|||
this.uid = null
|
||||
this.email = null
|
||||
this.verified = false
|
||||
this.verifier = null
|
||||
this.salt = null
|
||||
this.kA = null
|
||||
this.wrapKb = null
|
||||
this.params = null
|
||||
this.srp = null
|
||||
this.passwordStretching = null
|
||||
// references
|
||||
this.resetTokenId = null
|
||||
this.sessionTokenIds = null
|
||||
|
@ -30,11 +29,10 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
|
|||
a.uid = object.uid
|
||||
a.email = object.email
|
||||
a.verified = !!object.verified
|
||||
a.verifier = object.verifier
|
||||
a.salt = object.salt
|
||||
a.srp = object.srp
|
||||
a.kA = object.kA
|
||||
a.wrapKb = object.wrapKb
|
||||
a.params = object.params
|
||||
a.passwordStretching = object.passwordStretching
|
||||
a.resetTokenId = object.resetTokenId
|
||||
a.sessionTokenIds = object.sessionTokenIds || {}
|
||||
a.recoveryMethodIds = object.recoveryMethodIds || {}
|
||||
|
@ -232,8 +230,8 @@ module.exports = function (P, tokens, RecoveryMethod, db, config, error) {
|
|||
Account.prototype.reset = function (form) {
|
||||
//this.kA = null // TODO
|
||||
this.wrapKb = form.wrapKb
|
||||
this.verifier = form.verifier
|
||||
this.params = form.params
|
||||
this.srp = form.srp
|
||||
this.passwordStretching = form.passwordStretching
|
||||
return this.deleteAllTokens()
|
||||
.then(this.save.bind(this))
|
||||
}
|
||||
|
|
|
@ -40,8 +40,8 @@ module.exports = function (P, uuid, srp, db, error) {
|
|||
session.uid = account.uid
|
||||
session.N = srp.params[2048].N
|
||||
session.g = srp.params[2048].g
|
||||
session.s = account.salt
|
||||
session.v = Buffer(account.verifier, 'hex')
|
||||
session.s = account.srp.salt
|
||||
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
|
||||
|
|
|
@ -19,18 +19,17 @@ module.exports = function (crypto, uuid, isA, error, Account, RecoveryMethod) {
|
|||
validate: {
|
||||
payload: {
|
||||
email: isA.String().email().required(),
|
||||
verifier: isA.String().regex(HEX_STRING).required(),
|
||||
salt: isA.String().regex(HEX_STRING).required(),
|
||||
params: isA.Object({
|
||||
srp: isA.Object({
|
||||
alg: isA.String().valid('sha256').required(),
|
||||
N_bits: isA.Number().valid(2048).required()
|
||||
}).required(),
|
||||
stretch: isA.Object({
|
||||
salt: isA.String().regex(HEX_STRING).required(),
|
||||
rounds: isA.Number().required()
|
||||
})
|
||||
})
|
||||
srp: isA.Object({
|
||||
type: isA.String().required(), // TODO valid()
|
||||
verifier: isA.String().regex(HEX_STRING).required(),
|
||||
salt: isA.String().regex(HEX_STRING).required(),
|
||||
}).required(),
|
||||
passwordStretching: isA.Object(
|
||||
// {
|
||||
// type: isA.String().required(),
|
||||
// salt: isA.String().regex(HEX_STRING).required()
|
||||
// }
|
||||
)
|
||||
}
|
||||
},
|
||||
handler: function accountCreate(request) {
|
||||
|
@ -40,11 +39,10 @@ module.exports = function (crypto, uuid, isA, error, Account, RecoveryMethod) {
|
|||
{
|
||||
uid: uuid.v4(),
|
||||
email: form.email,
|
||||
verifier: form.verifier,
|
||||
salt: form.salt,
|
||||
srp: form.srp,
|
||||
kA: crypto.randomBytes(32).toString('hex'),
|
||||
wrapKb: crypto.randomBytes(32).toString('hex'),
|
||||
params: form.params
|
||||
passwordStretching: form.passwordStretching
|
||||
}
|
||||
)
|
||||
.done(
|
||||
|
|
|
@ -17,8 +17,10 @@ var AccountResetToken = models.tokens.AccountResetToken
|
|||
var a = {
|
||||
uid: 'xxx',
|
||||
email: 'somebody@example.com',
|
||||
verifier: 'BAD1',
|
||||
salt: 'BAD2',
|
||||
srp: {
|
||||
verifier: 'BAD1',
|
||||
salt: 'BAD2'
|
||||
},
|
||||
kA: 'BAD3',
|
||||
wrapKb: 'BAD4'
|
||||
}
|
||||
|
@ -412,8 +414,12 @@ test(
|
|||
function (t) {
|
||||
var form = {
|
||||
wrapKb: 'DEADBEEF',
|
||||
verifier: 'FEEDFACE',
|
||||
params: {
|
||||
srp: {
|
||||
type: 'SRP-6a/SHA256/2048/v1',
|
||||
verifier: 'FEEDFACE',
|
||||
salt: '12345678'
|
||||
},
|
||||
passwordStretching: {
|
||||
stuff: true
|
||||
}
|
||||
}
|
||||
|
@ -426,7 +432,7 @@ test(
|
|||
.then(
|
||||
function (account) {
|
||||
t.equal(account.wrapKb, form.wrapKb)
|
||||
t.equal(account.verifier, form.verifier)
|
||||
t.equal(account.srp.verifier, form.srp.verifier)
|
||||
}
|
||||
)
|
||||
.then(Account.del.bind(null, a.uid))
|
||||
|
@ -445,8 +451,12 @@ test(
|
|||
var reset = null
|
||||
var form = {
|
||||
wrapKb: 'DEADBEEF',
|
||||
verifier: 'FEEDFACE',
|
||||
params: {
|
||||
srp: {
|
||||
type: 'SRP-6a/SHA256/2048/v1',
|
||||
verifier: 'FEEDFACE',
|
||||
salt: '12345678'
|
||||
},
|
||||
passwordStretching: {
|
||||
stuff: true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,14 +17,16 @@ var alice = {
|
|||
uid: 'xxx',
|
||||
email: 'somebody@example.com',
|
||||
password: 'awesomeSauce',
|
||||
verifier: null,
|
||||
salt: 'BAD1',
|
||||
srp: {
|
||||
verifier: null,
|
||||
salt: 'BAD1'
|
||||
},
|
||||
kA: 'BAD3',
|
||||
wrapKb: 'BAD4'
|
||||
}
|
||||
|
||||
alice.verifier = srp.getv(
|
||||
Buffer(alice.salt, 'hex'),
|
||||
alice.srp.verifier = srp.getv(
|
||||
Buffer(alice.srp.salt, 'hex'),
|
||||
Buffer(alice.email),
|
||||
Buffer(alice.password),
|
||||
srp.params[2048].N,
|
||||
|
@ -44,7 +46,7 @@ Account.create(alice)
|
|||
.done(
|
||||
function (s) {
|
||||
t.equal(s.uid, a.uid)
|
||||
t.equal(s.s, a.salt)
|
||||
t.equal(s.s, a.srp.salt)
|
||||
t.equal(s.type, 'login')
|
||||
|
||||
t.end()
|
||||
|
|
Загрузка…
Ссылка в новой задаче