Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to manually checking transaction state #27

Merged
merged 16 commits into from
Mar 7, 2017
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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contructor...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch


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;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contructor

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch


module.exports = UnexpectedInputError;
31 changes: 25 additions & 6 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ var auth0Ticket = require('./utils/auth0_ticket');
var httpClient = require('./utils/http_client');
var transactionFactory = require('./transaction/factory');
var clientFactory = require('./utils/client_factory');

var apiTransport = {
polling: 'polling',
manual: 'polling',
socket: 'socket'
};

/**
* @public
*
Expand All @@ -26,7 +33,9 @@ var clientFactory = require('./utils/client_factory');
* @param {string} [options.globalTrackingId] Id used to associate the request
* in the transaction (that includes both Guardian and Auth0-server requests)
* @param {string} options.accountLabel
* @param {socket|polling} [options.transport]
*
* @deprecated @param {string} [options.transport] Use stateCheckingMechanism
* @param {string} [options.stateCheckingMechanism]
*
* @param {SocketClient} [options.dependencies.socketClient] Client factory for socket api
* @param {function(serviceUrl)} [options.dependencies.httpClient] Client factory for http
Expand All @@ -44,7 +53,7 @@ function auth0GuardianJS(options) {
self.httpClient = object.get(options, 'dependencies.httpClient',
httpClient(self.serviceUrl, globalTrackingId));

self.transport = options.transport || 'socket';
self.transport = options.transport || options.state_checking_mechanism || 'socket';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jsdoc says stateCheckingMechanism; I don't see anything convering it to snake_case...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch


self.socketClient = clientFactory.create({
serviceUrl: self.serviceUrl,
Expand Down Expand Up @@ -81,7 +90,9 @@ auth0GuardianJS.prototype.start = function start(callback) {

self.httpClient.post('/api/start-flow',
self.credentials,
{ state_transport: self.transport },
// TODO: polling is not a good name for api state checking since
// it could be polling or manual checking
{ state_transport: apiTransport[self.transport] },
function startTransaction(err, txLegacyData) {
if (err) {
callback(err);
Expand Down Expand Up @@ -138,19 +149,27 @@ auth0GuardianJS.prototype.start = function start(callback) {
*
* @param {string} transactionState.baseUrl

* @param {undefined|string} options.transport
* @deprecated @param {string} [options.transport] Use stateCheckingMechanism
* @param {string} [options.stateCheckingMechanism]
*
* @param {SocketClient} [options.dependencies.socketClient] Client factory for socket api
* @param {function(serviceUrl)} [options.dependencies.httpClient] Client factory for http
*/

auth0GuardianJS.resume = function resume(options, transactionState, callback) {
var txId = jwtToken(transactionState.transactionToken).getDecoded().txid;
var transactionTokenObject = jwtToken(transactionState.transactionToken);
var txId = transactionTokenObject.getDecoded().txid;

// create httpClient/socketClient
var httpClientInstance = object.get(options, 'dependencies.httpClient',
httpClient(transactionState.baseUrl, txId));

var transport = options.transport || 'socket';
var transport = options.transport || options.stateCheckingMechanism || 'socket';

if (transactionTokenObject.isExpired()) {
asyncHelpers.setImmediate(callback, new errors.CredentialsExpiredError());
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<3


var socketClient = clientFactory.create({
serviceUrl: transactionState.baseUrl,
Expand Down
35 changes: 32 additions & 3 deletions lib/transaction/auth_verification_step.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var object = require('../utils/object');
var asyncHelpers = require('../utils/async');
var EventEmitter = require('events').EventEmitter;
var helpers = require('./helpers');
var events = require('../utils/events');

function authVerificationStep(strategy, options) {
var self = object.create(authVerificationStep.prototype);
Expand All @@ -28,7 +29,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,14 +67,27 @@ authVerificationStep.prototype.verify = function verify(data) {
verificationPayload = verificationPayload || {};

if (err) {
if (acceptedCallback) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can this ever be false? I see this before

acceptedCallback = acceptedCallback || function noop() {};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point forgot to remove that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

events.ignoreError(self, err);

acceptedCallback(err);
}

done(err);
return;
}

done(null, {
var payload = {
// New recovery code if needed (recover)
recoveryCode: verificationPayload.recoveryCode
});
};

// 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, payload);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like this actually needs to be always there, so the previous if is not required ?

done(null, payload);
});
};

Expand All @@ -88,4 +111,10 @@ authVerificationStep.prototype.verify = function verify(data) {
});
};

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

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

module.exports = authVerificationStep;
36 changes: 34 additions & 2 deletions lib/transaction/enrollment_confirmation_step.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var object = require('../utils/object');
var asyncHelpers = require('../utils/async');
var events = require('../utils/events');
var EventEmitter = require('events').EventEmitter;
var enrollmentBuilder = require('../entities/enrollment');
var helpers = require('./helpers');
Expand Down Expand Up @@ -31,7 +32,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 +56,21 @@ enrollmentConfirmationStep.prototype.confirm = function confirm(data) {
};

var confirmTask = function confirmTask(done) {
self.strategy.confirm(data, done);
self.strategy.confirm(data, function confirmationDataAccepted(err, result) {
if (err) {
if (acceptedCallback) {
events.ignoreError(self, err);

acceptedCallback(err);
}

done(err);
return;
}

acceptedCallback(err, { recoveryCode: self.enrollmentAttempt.getRecoveryCode() });
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing the recovery code here don't quite convince me, but, how are you going to get the recovery code otherwise... making self.enrollmentAttempt.getRecoveryCode() public seems like opening the door too much considering that we are going to change how enrollmentAttempt works.

done(err, result);
});
};

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

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

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

module.exports = enrollmentConfirmationStep;
10 changes: 9 additions & 1 deletion lib/transaction/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,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 +69,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
Loading