1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
7 this.EXPORTED_SYMBOLS = ["MobileIdentityVerificationFlow"];
9 const { utils: Cu, classes: Cc, interfaces: Ci } = Components;
11 Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
12 Cu.import("resource://gre/modules/Promise.jsm");
13 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
15 this.MobileIdentityVerificationFlow = function(aVerificationOptions,
20 this.verificationOptions = aVerificationOptions;
22 this.client = aClient;
23 this.retries = VERIFICATIONCODE_RETRIES;
24 this.verifyStrategy = aVerifyStrategy;
25 this.cleanupStrategy = aCleanupStrategy;
28 MobileIdentityVerificationFlow.prototype = {
30 doVerification: function() {
31 log.debug("Start verification flow");
32 return this.register()
35 log.debug("Register result ${}", registerResult);
36 if (!registerResult || !registerResult.msisdnSessionToken) {
37 return Promise.reject(ERROR_INTERNAL_UNEXPECTED);
39 this.sessionToken = registerResult.msisdnSessionToken;
40 // We save the timestamp of the start of the verification timeout to be
41 // able to provide to the UI the remaining time on each retry.
43 log.debug("Creating verification code timer");
44 this.timerCreation = Date.now();
45 this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
46 this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
47 VERIFICATIONCODE_TIMEOUT,
48 this.timer.TYPE_ONE_SHOT);
51 if (!this.verifyStrategy) {
52 return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
55 return this.verifyStrategy()
57 return this._doVerification();
59 this.verificationCodeDeferred.reject(reason);
65 _doVerification: function() {
66 log.debug("_doVerification");
68 this.verificationCodeDeferred = Promise.defer();
70 // If the verification flow can be for an external phone number,
71 // we need to ask the user for the verification code.
72 // In that case we don't do a notification about the verification
73 // process being done until the user enters the verification code
75 if (this.verificationOptions.external) {
78 timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
81 this.ui.verificationCodePrompt(this.retries,
82 VERIFICATIONCODE_TIMEOUT / 1000,
85 (verificationCode) => {
86 if (!verificationCode) {
87 return this.verificationCodeDeferred.reject(
88 ERROR_INTERNAL_INVALID_PROMPT_RESULT);
90 // If the user got the verification code that means that the
91 // introduced phone number didn't belong to any of the inserted
94 this.verificationCodeDeferred.resolve(verificationCode);
101 return this.verificationCodeDeferred.promise.then(
102 this.onVerificationCode.bind(this)
106 // When we receive a verification code from the UI, we check it against
107 // the server. If the verification code is incorrect, we decrease the
108 // number of retries left and allow the user to try again. If there is no
109 // possible retry left, we notify about this error so the UI can allow the
110 // user to request the resend of a new verification code.
111 onVerificationCode: function(aVerificationCode) {
112 log.debug("onVerificationCode " + aVerificationCode);
113 if (!aVerificationCode) {
114 this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
115 return this._doVerification();
118 // Before checking the verification code against the server we set the
119 // "verifying" flag to queue timeout expiration events received before
120 // the server request is completed. If the server request is positive
121 // we will discard the timeout event, otherwise we will progress the
122 // event to the UI to allow the user to retry.
123 this.verifying = true;
125 return this.verifyCode(aVerificationCode)
129 return Promise.reject(INTERNAL_UNEXPECTED);
131 // The code was correct!
132 // At this point the phone number is verified.
133 // We return the given verification options with the session token
134 // to be stored in the credentials store. With this data we will be
135 // asking the server to give us a certificate to generate assertions.
136 this.verificationOptions.sessionToken = this.sessionToken;
137 this.verificationOptions.msisdn = result.msisdn ||
138 this.verificationOptions.msisdn;
139 return this.verificationOptions;
142 log.error("Verification code error " + error);
144 log.error("Retries left " + this.retries);
146 this.ui.error(ERROR_NO_RETRIES_LEFT);
149 return Promise.reject(ERROR_NO_RETRIES_LEFT);
151 this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
152 this.verifying = false;
153 if (this.queuedTimeout) {
154 this.onVerificationCodeTimeout();
156 return this._doVerification();
161 onVerificationCodeTimeout: function() {
162 // It is possible that we get the timeout when we are checking a
163 // verification code with the server. In that case, we queue the
164 // timeout to be triggered after we receive the reply from the server
166 if (this.verifying) {
167 this.queuedTimeout = true;
171 // When the verification process times out we do a clean up, reject
172 // the corresponding promise and notify the UI about the timeout.
173 if (this.verificationCodeDeferred) {
174 this.verificationCodeDeferred.reject(ERROR_VERIFICATION_CODE_TIMEOUT);
176 this.ui.error(ERROR_VERIFICATION_CODE_TIMEOUT);
179 register: function() {
180 return this.client.register();
183 verifyCode: function(aVerificationCode) {
184 return this.client.verifyCode(this.sessionToken, aVerificationCode);
187 unregister: function() {
188 return this.client.unregister(this.sessionToken);
191 cleanup: function(aUnregister = false) {
192 log.debug("Verification flow cleanup");
194 this.queuedTimeout = false;
195 this.retries = VERIFICATIONCODE_RETRIES;
206 this.sessionToken = null;
211 if (this.cleanupStrategy) {
212 this.cleanupStrategy();