Skip to content

Commit

Permalink
Add serialization and behavior changes
Browse files Browse the repository at this point in the history
  • Loading branch information
dafortune committed Mar 3, 2017
1 parent 588d883 commit 8063ad0
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 5 deletions.
2 changes: 2 additions & 0 deletions lib/errors/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ exports.NotEnrolledError = require('./not_enrolled_error');
exports.AuthMethodDisabledError = require('./auth_method_disabled_error');
exports.EnrollmentMethodDisabledError = require('./enrollment_method_disabled_error');
exports.InvalidEnrollmentError = require('./invalid_enrollment_error');
exports.InvalidStateError = require('./invalid_state_error');
exports.UnexpectedInputError = require('./unexpected_input_error');
16 changes: 16 additions & 0 deletions lib/errors/invalid_state_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

var object = require('../utils/object');
var GuardianError = require('./guardian_error');

function InvalidStateError(message) {
GuardianError.call(this, {
message: message,
errorCode: 'invalid_state'
});
}

InvalidStateError.prototype = object.create(GuardianError.prototype);
InvalidStateError.prototype.contructor = InvalidStateError;

module.exports = InvalidStateError;
16 changes: 16 additions & 0 deletions lib/errors/unexpected_input_error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
'use strict';

var object = require('../utils/object');
var GuardianError = require('./guardian_error');

function UnexpectedInputError(message) {
GuardianError.call(this, {
message: message,
errorCode: 'unexpected_input'
});
}

UnexpectedInputError.prototype = object.create(GuardianError.prototype);
UnexpectedInputError.prototype.contructor = UnexpectedInputError;

module.exports = UnexpectedInputError;
23 changes: 22 additions & 1 deletion lib/transaction/auth_verification_step.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,16 @@ authVerificationStep.prototype.getMethod = function getMethod() {
return this.strategy.method;
};

authVerificationStep.prototype.verify = function verify(data) {
/**
* @param {object} data
* @param {function} [acceptedCallback] Triggered once the data has been
* accepted by the service provider, the first arguments indicates if it
* was valid
*/
authVerificationStep.prototype.verify = function verify(data, acceptedCallback) {
// eslint-disable-next-line no-param-reassign
acceptedCallback = acceptedCallback || function noop() {};

var self = this;

// TODO Move this to the transaction
Expand Down Expand Up @@ -57,10 +66,16 @@ authVerificationStep.prototype.verify = function verify(data) {
verificationPayload = verificationPayload || {};

if (err) {
acceptedCallback(err);
done(err);
return;
}

// On the one hand we trigger the callback since the data has been
// accepted by the service provider, on the other hand
// we still need to wait for (loginOrRejectTask) to trigger the event.
// loginOrRejectTask might never be triggered in case of transport=manual
acceptedCallback(null, verificationPayload.accepted);
done(null, {
// New recovery code if needed (recover)
recoveryCode: verificationPayload.recoveryCode
Expand Down Expand Up @@ -88,4 +103,10 @@ authVerificationStep.prototype.verify = function verify(data) {
});
};

authVerificationStep.prototype.serialize = function serialize() {
var self = this;

return { method: self.getMethod() };
};

module.exports = authVerificationStep;
24 changes: 22 additions & 2 deletions lib/transaction/enrollment_confirmation_step.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ enrollmentConfirmationStep.prototype.getData = function getData() {
return this.strategy.getData();
};

enrollmentConfirmationStep.prototype.confirm = function confirm(data) {
enrollmentConfirmationStep.prototype.getMethod = function getMethod() {
return this.strategy.method;
};

/**
* @param {object} data
* @param {function} [acceptedCallback] Triggered once the data has been accepted by
* the service provider, the first argument indicates if it was valid
*/
enrollmentConfirmationStep.prototype.confirm = function confirm(data, acceptedCallback) {
// eslint-disable-next-line no-param-reassign
acceptedCallback = acceptedCallback || function noop() {};
var self = this;

// TODO Move this to the transaction
Expand All @@ -44,7 +55,10 @@ enrollmentConfirmationStep.prototype.confirm = function confirm(data) {
};

var confirmTask = function confirmTask(done) {
self.strategy.confirm(data, done);
self.strategy.confirm(data, function confirmationDataAccepted(err, result) {
acceptedCallback(err, result.accepted);
done(err, result);
});
};

asyncHelpers.all([
Expand Down Expand Up @@ -73,4 +87,10 @@ enrollmentConfirmationStep.prototype.confirm = function confirm(data) {
});
};

enrollmentConfirmationStep.prototype.serialize = function serialize() {
var self = this;

return { method: self.getMethod() };
};

module.exports = enrollmentConfirmationStep;
12 changes: 11 additions & 1 deletion lib/transaction/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ var enrollment = require('../entities/enrollment');
var transaction = require('./');
var jwtToken = require('../utils/jwt_token');
var object = require('../utils/object');
var enrollmentConfirmationStepBuilder = require('./enrollment_confirmation_step');
var authVerificationStepBuilder = require('./auth_verification_step');

exports.fromStartFlow = function buildTransactionFromStartFlow(data, options) {
var txLegacyData = data.txLegacyData;
Expand Down Expand Up @@ -54,6 +56,12 @@ exports.fromStartFlow = function buildTransactionFromStartFlow(data, options) {
* @param {EventEmitter} options.transactionEventsReceiver
* Receiver for transaction events; it will receive backend related transaction events
*
* @param {object} transactionState.authVerificationStep
* @param {otp|push|sms} transactionState.authVerificationStep.method
*
* @param {object} transactionState.enrollmentConfirmationStep
* @param {otp|push|sms} transactionState.enrollmentConfirmationStep.method
*
* @param {HttpClient} options.HttpClient
*
* @returns {Transaction}
Expand All @@ -63,7 +71,9 @@ exports.fromTransactionState = function fromTransactionState(transactionState, o
transactionToken: jwtToken(transactionState.transactionToken),
enrollments: object.map(transactionState.enrollments, enrollment),
availableEnrollmentMethods: transactionState.availableEnrollmentMethods,
availableAuthenticationMethods: transactionState.availableAuthenticationMethods
availableAuthenticationMethods: transactionState.availableAuthenticationMethods,
authVerificationStep: transactionState.authVerificationStep,
enrollmentConfirmationStep: transactionState.enrollmentConfirmationStep
};

if (transactionState.enrollmentAttempt) {
Expand Down
101 changes: 100 additions & 1 deletion lib/transaction/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ var otpAuthStrategy = require('../auth_strategies/otp_auth_strategy');
var eventSequencer = require('../utils/event_sequencer');
var eventListenerHub = require('../utils/event_listener_hub');

var VALID_METHODS = ['otp', 'sms', 'push'];

/**
* @public
*
Expand All @@ -34,6 +36,8 @@ var eventListenerHub = require('../utils/event_listener_hub');
* Enrollment attempt (when you are not enrolled)
* @param {array.<string>} data.availableEnrollmentMethods
* @param {JWTToken} data.transactionToken
* @param {AuthVerificationStep} [data.authVerificationStep]
* @param {EnrollmentConfirmationStep} [data.enrollmentConfirmationStep]
*
* @param {EventEmitter} options.transactionEventsReceiver
* Receiver for transaction events; it will receive backend related transaction events
Expand Down Expand Up @@ -133,11 +137,79 @@ function transaction(data, options) {
self.eventSequencer.emit('error', err);
});

self.authVerificationStep = data.authVerificationStep;
self.enrollmentConfirmationStep = data.enrollmentConfirmationStep;

if (data.enrollmentConfirmationStep && data.enrollmentConfirmationStep.method) {
self.enrollmentConfirmationStep = self.buildEnrollmentConfirmationStep(
data.enrollmentConfirmationStep);
}

if (data.authVerificationStep && data.authVerificationStep.method) {
self.authVerificationStep = self.buildAuthVerificationStep(
data.authVerificationStep);
}

return self;
}

transaction.prototype = object.create(EventEmitter.prototype);

/**
* @param {object} data
* @param {otp|push|sms} data.method
*/
transaction.prototype.buildAuthVerificationStep = function buildAuthVerificationStep(data) {
var self = this;

if (typeof data !== 'object') {
throw new errors.UnexpectedInputError('Expected data to be an object');
}

if (typeof VALID_METHODS.indexOf(data.method) < 0) {
throw new errors.UnexpectedInputError('Expected data.method to be one of ' +
VALID_METHODS.join(', ') + ' but found ' + data.method);
}

if (!self.isEnrolled()) {
throw new errors.InvalidStateError('Expected user to be enrolled');
}

return authVerificationStep(self.authStrategies[data.method], {
loginCompleteHub: self.loginCompleteHub,
loginRejectedHub: self.loginRejectedHub
});
};

/**
* @param {object} data
* @param {otp|push|sms} data.method
*/
transaction.prototype.buildEnrollmentConfirmationStep =
function buildEnrollmentConfirmationStep(data) {
var self = this;

if (typeof data !== 'object') {
throw new errors.UnexpectedInputError('Expected data to be an object');
}

if (typeof VALID_METHODS.indexOf(data.method) < 0) {
throw new errors.UnexpectedInputError('Expected data.method to be one of ' +
VALID_METHODS.join(', ') + ' but found ' + data.method);
}

if (!self.enrollmentAttempt) {
throw new errors.InvalidStateError('Expected enrollment attempt to be present: ' +
'try calling .enroll method first');
}

return enrollmentConfirmationStep({
strategy: self.enrollmentStrategies[data.method],
enrollmentAttempt: self.enrollmentAttempt,
enrollmentCompleteHub: transaction.enrollmentCompleteHub
});
};

/**
* @Public
*
Expand All @@ -152,14 +224,21 @@ transaction.prototype.serialize = function serialize() {
return enrollment.serialize();
}

return {
var data = {
transactionToken: this.transactionToken.getToken(),
enrollmentAttempt: this.enrollmentAttempt ? this.enrollmentAttempt.serialize() : undefined,
enrollments: object.map(this.enrollments, enrollmentSerialize),
baseUrl: this.httpClient.getBaseUrl(),
availableEnrollmentMethods: this.availableEnrollmentMethods,
availableAuthenticationMethods: this.availableAuthenticationMethods
};

data.authVerificationStep = self.authVerificationStep
? self.authVerificationStep.serialize() : null;
data.enrollmentConfirmationStep = self.enrollmentConfirmationStep
? self.enrollmentConfirmationStep.serialize() : null;

return data;
};


Expand Down Expand Up @@ -414,8 +493,28 @@ transaction.prototype.requestStrategyAuth = function requestStrategyAuth(strateg
self.eventSequencer.emit('error', error);
});

self.authVerificationStep = verificationStep;

callback(null, verificationStep);
});
};

transaction.prototype.getAuthVerficationStep = function getAuthVerficationStep() {
if (!self.authVerificationStep) {
throw new errors.InvalidStateError('cannot get enrollment confirmation ' +
'step, you must call .requestAuth first');
}

return self.authVerificationStep;
};

transaction.prototype.getEnrollmentConfirmationStep = function getEnrollmentConfirmationStep() {
if (!self.enrollmentConfirmationStep) {
throw new errors.InvalidStateError('cannot get enrollment confirmation step, ' +
'you must call .enroll first');
}

return self.enrollmentConfirmationStep;
};

module.exports = transaction;

0 comments on commit 8063ad0

Please sign in to comment.