implemented sending of one-time-passwords

This commit is contained in:
tigerbot 2017-07-21 10:45:01 -06:00
parent e36689600c
commit d20e40203e
2 changed files with 82 additions and 3 deletions

View File

@ -7,19 +7,21 @@ module.exports = [
{
tablename: apiname + '_private_keys',
idname: 'id',
unique: ['id'],
indices: baseFields.concat([ 'kty', 'kid' ]),
},
{
tablename: apiname + '_codes',
idname: 'id',
indices: baseFields.concat([ 'code', 'expires' ]),
},
{
tablename: apiname + '_jwks',
idname: 'id',
unique: ['id'],
indices: baseFields.concat([ 'kty', 'kid', 'sub' ]),
},
{
tablename: apiname + '_grants',
idname: 'id',
unique: ['id'],
indices: baseFields.concat([ 'sub', 'azp', 'azpSub', 'scope' ]),
},
];

77
rest.js
View File

@ -212,6 +212,22 @@ module.exports.create = function (bigconf, deps, app) {
app.handlePromise(req, res, promise, '[issuer@oauth3.org] save grants');
};
Tokens.retrieveOtpCode = function (codeStore, codeId) {
return codeStore.get(codeId).then(function (code) {
if (!code) {
return null;
}
var expires = (new Date(code.expires)).valueOf();
if (!expires || Date.now() > expires) {
return codeStore.destroy(codeId).then(function () {
return null;
});
}
return code;
});
};
Tokens.getPrivKey = function (store, experienceId) {
return store.IssuerOauth3OrgPrivateKeys.get(experienceId).then(function (jwk) {
if (jwk) {
@ -235,6 +251,66 @@ module.exports.create = function (bigconf, deps, app) {
});
};
Tokens.restful.sendOtp = function (req, res) {
var params = req.body;
var promise = PromiseA.resolve().then(function () {
if (!params || !params.username) {
throw new Error("must provide the email address as 'username' in the body");
}
if ((params.username_type && 'email' !== params.username_type) || !/@/.test(params.username)) {
throw new Error("only email one-time login codes are supported at this time");
}
params.username_type = 'email';
return req.getSiteStore();
}).then(function (store) {
var codeStore = store.IssuerOauth3OrgCodes;
var codeId = crypto.createHash('sha256').update(params.username_type+':'+params.username).digest('base64');
codeId = makeB64UrlSafe(codeId);
return Tokens.retrieveOtpCode(codeStore, codeId).then(function (code) {
if (code) {
return code;
}
var token = '';
while (!/^\d{4}-\d{4}-\d{4}$/.test(token)) {
// Most of the number we can generate this was start with 1 (and no matter what can't
// start with 0), so we don't use the very first digit. Also basically all of the
// numbers are too big to accurately store in JS floats, so we limit the trailing 0's.
token = (parseInt(crypto.randomBytes(8).toString('hex'), 16)).toString()
.replace(/0+$/, '0').replace(/\d(\d{4})(\d{4})(\d{4}).*/, '$1-$2-$3');
}
code = {
id: codeId,
code: token,
expires: new Date(Date.now() + 20*60*1000),
};
return codeStore.upsert(codeId, code).then(function (){
return code;
});
});
}).then(function (code) {
var emailParams = {
to: params.username,
from: 'login@daplie.com', // opts.mailer.defaults.system
replyTo: 'hello@daplie.com',
subject: "Use " + code.code + " as your Login Code", // message.Subject
text: code.code + " is your Login Code." // message['stripped-html']
};
emailParams['h:Reply-To'] = emailParams.replyTo;
return req.getSiteMailer().sendMailAsync(emailParams).then(function () {
return {
id: code.id,
expires: code.expires,
created: new Date(parseInt(code.createdAt, 10) || code.createdAt),
};
});
});
app.handlePromise(req, res, promise, '[issuer@oauth3.org] send one-time-password');
};
Tokens.restful.create = function (req, res) {
var store;
var promise = req.getSiteStore().then(function (_store) {
@ -288,6 +364,7 @@ module.exports.create = function (bigconf, deps, app) {
app.get( '/grants/:sub/:azp', Grants.restful.getOne);
app.post( '/grants/:sub/:azp', Grants.restful.saveNew);
app.post( '/access_token/send_otp', Tokens.restful.sendOtp);
app.use( '/access_token/:sub', authorizeIssuer);
app.post( '/access_token/:sub/:aud/:azp', Tokens.restful.create);