-
Notifications
You must be signed in to change notification settings - Fork 39
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
Changes from 5 commits
588d883
a241a1d
e5bab1a
b29b3c2
06c4ce7
d77033d
8fed2bf
2e3b535
e1f0822
e649bff
8294dc7
0015778
6a3832f
2291036
0d6d662
6d9b19e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; |
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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch |
||
|
||
module.exports = UnexpectedInputError; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
* | ||
|
@@ -81,7 +88,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); | ||
|
@@ -144,14 +153,20 @@ auth0GuardianJS.prototype.start = function start(callback) { | |
*/ | ||
|
||
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'; | ||
|
||
if (transactionTokenObject.isExpired()) { | ||
asyncHelpers.setImmediate(callback, new errors.CredentialsExpiredError()); | ||
return; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. <3 |
||
|
||
var socketClient = clientFactory.create({ | ||
serviceUrl: transactionState.baseUrl, | ||
transport: transport, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
|
@@ -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, { recoveryCode: self.enrollmentAttempt.getRecoveryCode() }); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
done(err, result); | ||
}); | ||
}; | ||
|
||
asyncHelpers.all([ | ||
|
@@ -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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
* | ||
|
@@ -35,6 +37,12 @@ var eventListenerHub = require('../utils/event_listener_hub'); | |
* @param {array.<string>} data.availableEnrollmentMethods | ||
* @param {JWTToken} data.transactionToken | ||
* | ||
* @param {object} [data.authVerificationStep] | ||
* @param {otp|push|sms} data.authVerificationStep.method | ||
* | ||
* @param {object} [data.enrollmentConfirmationStep] | ||
* @param {otp|push|sms} data.enrollmentConfirmationStep.method | ||
* | ||
* @param {EventEmitter} options.transactionEventsReceiver | ||
* Receiver for transaction events; it will receive backend related transaction events | ||
* @param {HttpClient} options.HttpClient | ||
|
@@ -133,11 +141,80 @@ 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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test for invalid method in the serialized transaction |
||
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, | ||
transaction: self | ||
}); | ||
}; | ||
|
||
/** | ||
* @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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add test for invalid method in the serialized transaction |
||
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: self.enrollmentCompleteHub, | ||
transaction: self | ||
}); | ||
}; | ||
|
||
/** | ||
* @Public | ||
* | ||
|
@@ -148,18 +225,27 @@ transaction.prototype = object.create(EventEmitter.prototype); | |
* | ||
*/ | ||
transaction.prototype.serialize = function serialize() { | ||
var self = this; | ||
|
||
function enrollmentSerialize(enrollment) { | ||
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; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. move 236, to: data.enrollmentAttempt: this.enrollmentAttempt ? this.enrollmentAttempt.serialize() : undefined |
||
return data; | ||
}; | ||
|
||
|
||
|
@@ -220,6 +306,8 @@ transaction.prototype.enroll = function enroll(method, data, callback) { | |
enrollmentAttempt: self.enrollmentAttempt | ||
}); | ||
|
||
self.enrollmentConfirmationStep = confirmationStep; | ||
|
||
confirmationStep.once('enrollment-complete', function onEnrollmentComplete(payload) { | ||
self.addEnrollment(payload.enrollment); | ||
self.eventSequencer.emit('enrollment-complete', payload); | ||
|
@@ -414,8 +502,28 @@ transaction.prototype.requestStrategyAuth = function requestStrategyAuth(strateg | |
self.eventSequencer.emit('error', error); | ||
}); | ||
|
||
self.authVerificationStep = verificationStep; | ||
|
||
callback(null, verificationStep); | ||
}); | ||
}; | ||
|
||
transaction.prototype.getAuthVerficationStep = function getAuthVerficationStep() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be easily converted in @nikolaseu proposal of adding the method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo in "getAuthVerficationStep()", missing "i" in verification There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
if (!self.authVerificationStep) { | ||
throw new errors.InvalidStateError('cannot get enrollment confirmation ' + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consistent capitalization -- "Cannot get..." There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
'step, you must call .requestAuth first'); | ||
} | ||
|
||
return self.authVerificationStep; | ||
}; | ||
|
||
transaction.prototype.getEnrollmentConfirmationStep = function getEnrollmentConfirmationStep() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This can be easily converted in @nikolaseu proposal of adding the method confirmEnrollment to the transaction, just call getEnrollmentConfirmationStep().confirm(...) on that method. |
||
if (!self.enrollmentConfirmationStep) { | ||
throw new errors.InvalidStateError('cannot get enrollment confirmation step, ' + | ||
'you must call .enroll first'); | ||
} | ||
|
||
return self.enrollmentConfirmationStep; | ||
}; | ||
|
||
module.exports = transaction; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
var polling = require('./polling_client'); | ||
var socketio = require('./socket_client'); | ||
var nullClient = require('./null_client'); | ||
|
||
exports.create = function create(options) { | ||
var serviceUrl = options.serviceUrl; | ||
|
@@ -13,6 +14,8 @@ exports.create = function create(options) { | |
|
||
if (transport === 'polling') { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Need to replace this by @thoper proposed name. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We support both, internally I keep using transport not to spread the changes. |
||
return polling(serviceUrl, { httpClient: httpClient }); | ||
} else if (transport === 'manual') { | ||
return nullClient(); | ||
} | ||
|
||
// default socket | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
contructor
...There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch