UNPKG

27.5 kBJavaScriptView Raw
1"use strict";
2var __extends = (this && this.__extends) || function (d, b) {
3 for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
4 function __() { this.constructor = d; }
5 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
6};
7var ionic_native_1 = require('ionic-native');
8var guards_1 = require('./guards');
9var errors_1 = require('./errors');
10var promise_1 = require('./promise');
11var util_1 = require('./util');
12/**
13 * @hidden
14 */
15var AuthTokenContext = (function () {
16 function AuthTokenContext(deps, label) {
17 this.label = label;
18 this.storage = deps.storage;
19 }
20 AuthTokenContext.prototype.get = function () {
21 return this.storage.get(this.label);
22 };
23 AuthTokenContext.prototype.store = function (token) {
24 this.storage.set(this.label, token);
25 };
26 AuthTokenContext.prototype.delete = function () {
27 this.storage.delete(this.label);
28 };
29 return AuthTokenContext;
30}());
31exports.AuthTokenContext = AuthTokenContext;
32/**
33 * @hidden
34 */
35var CombinedAuthTokenContext = (function () {
36 function CombinedAuthTokenContext(deps, label) {
37 this.label = label;
38 this.storage = deps.storage;
39 this.tempStorage = deps.tempStorage;
40 }
41 CombinedAuthTokenContext.prototype.get = function () {
42 var permToken = this.storage.get(this.label);
43 var tempToken = this.tempStorage.get(this.label);
44 var token = tempToken || permToken;
45 return token;
46 };
47 CombinedAuthTokenContext.prototype.store = function (token, options) {
48 if (options === void 0) { options = { 'permanent': true }; }
49 if (options.permanent) {
50 this.storage.set(this.label, token);
51 }
52 else {
53 this.tempStorage.set(this.label, token);
54 }
55 };
56 CombinedAuthTokenContext.prototype.delete = function () {
57 this.storage.delete(this.label);
58 this.tempStorage.delete(this.label);
59 };
60 return CombinedAuthTokenContext;
61}());
62exports.CombinedAuthTokenContext = CombinedAuthTokenContext;
63/**
64 * `Auth` handles authentication of a single user, such as signing up, logging
65 * in & out, social provider authentication, etc.
66 *
67 * @featured
68 */
69var Auth = (function () {
70 function Auth(deps) {
71 this.config = deps.config;
72 this.emitter = deps.emitter;
73 this.authModules = deps.authModules;
74 this.tokenContext = deps.tokenContext;
75 this.userService = deps.userService;
76 }
77 Object.defineProperty(Auth.prototype, "passwordResetUrl", {
78 /**
79 * Link the user to this URL for password resets. Only for email/password
80 * authentication.
81 *
82 * Use this if you want to use our password reset forms instead of creating
83 * your own in your app.
84 */
85 get: function () {
86 return this.config.getURL('web') + "/password/reset/" + this.config.get('app_id');
87 },
88 enumerable: true,
89 configurable: true
90 });
91 /**
92 * Check whether the user is logged in or not.
93 *
94 * If an auth token exists in local storage, the user is logged in.
95 */
96 Auth.prototype.isAuthenticated = function () {
97 var token = this.tokenContext.get();
98 if (token) {
99 return true;
100 }
101 return false;
102 };
103 /**
104 * Sign up a user with the given data. Only for email/password
105 * authentication.
106 *
107 * `signup` does not affect local data or the current user until `login` is
108 * called. This means you'll likely want to log in your users manually after
109 * signup.
110 *
111 * If a signup fails, the promise rejects with a [`IDetailedError`
112 * object](/api/client/idetailederror) that contains an array of error codes
113 * from the cloud.
114 *
115 * @param details - The details that describe a user.
116 */
117 Auth.prototype.signup = function (details) {
118 return this.authModules.basic.signup(details);
119 };
120 /**
121 * Attempt to log the user in with the given credentials. For custom & social
122 * logins, kick-off the authentication process.
123 *
124 * After login, the full user is loaded from the cloud and saved in local
125 * storage along with their auth token.
126 *
127 * @note TODO: Better error handling docs.
128 *
129 * @param moduleId
130 * The authentication provider module ID to use with this login.
131 * @param credentials
132 * For email/password authentication, give an email and password. For social
133 * authentication, exclude this parameter. For custom authentication, send
134 * whatever you need.
135 * @param options
136 * Options for this login, such as whether to remember the login and
137 * InAppBrowser window options for authentication providers that make use of
138 * it.
139 */
140 Auth.prototype.login = function (moduleId, credentials, options) {
141 var _this = this;
142 if (options === void 0) { options = {}; }
143 if (typeof options.remember === 'undefined') {
144 options.remember = true;
145 }
146 if (typeof options.inAppBrowserOptions === 'undefined') {
147 options.inAppBrowserOptions = {};
148 }
149 if (typeof options.inAppBrowserOptions.location === 'undefined') {
150 options.inAppBrowserOptions.location = false;
151 }
152 if (typeof options.inAppBrowserOptions.clearcache === 'undefined') {
153 options.inAppBrowserOptions.clearcache = true;
154 }
155 if (typeof options.inAppBrowserOptions.clearsessioncache === 'undefined') {
156 options.inAppBrowserOptions.clearsessioncache = true;
157 }
158 var context = this.authModules[moduleId];
159 if (!context) {
160 throw new Error('Authentication class is invalid or missing:' + context);
161 }
162 return context.authenticate(credentials, options).then(function (r) {
163 _this.storeToken(options, r.token);
164 return _this.userService.load().then(function () {
165 var user = _this.userService.current();
166 user.store();
167 return r;
168 });
169 });
170 };
171 /**
172 * Log the user out of the app.
173 *
174 * This clears the auth token out of local storage and restores the user to
175 * an unauthenticated state.
176 */
177 Auth.prototype.logout = function () {
178 this.tokenContext.delete();
179 var user = this.userService.current();
180 user.unstore();
181 user.clear();
182 };
183 /**
184 * Kick-off the password reset process. Only for email/password
185 * authentication.
186 *
187 * An email will be sent to the user with a short password reset code, which
188 * they can copy back into your app and use the [`confirmPasswordReset()`
189 * method](#confirmPasswordReset).
190 *
191 * @param email - The email address to which to send a code.
192 */
193 Auth.prototype.requestPasswordReset = function (email) {
194 this.storage.set('auth_password_reset_email', email);
195 return this.authModules.basic.requestPasswordReset(email);
196 };
197 /**
198 * Confirm a password reset.
199 *
200 * When the user gives you their password reset code into your app and their
201 * requested changed password, call this method.
202 *
203 * @param code - The password reset code from the user.
204 * @param newPassword - The requested changed password from the user.
205 */
206 Auth.prototype.confirmPasswordReset = function (code, newPassword) {
207 var email = this.storage.get('auth_password_reset_email');
208 if (!email) {
209 return promise_1.DeferredPromise.rejectImmediately(new Error('email address not found in local storage'));
210 }
211 else {
212 return this.authModules.basic.confirmPasswordReset(email, code, newPassword);
213 }
214 };
215 /**
216 * Get the raw auth token of the active user from local storage.
217 */
218 Auth.prototype.getToken = function () {
219 return this.tokenContext.get();
220 };
221 /**
222 * @hidden
223 */
224 Auth.prototype.storeToken = function (options, token) {
225 if (options === void 0) { options = { 'remember': true }; }
226 var originalToken = this.authToken;
227 this.authToken = token;
228 this.tokenContext.store(this.authToken, { 'permanent': options.remember });
229 this.emitter.emit('auth:token-changed', { 'old': originalToken, 'new': this.authToken });
230 };
231 /**
232 * @hidden
233 */
234 Auth.getDetailedErrorFromResponse = function (res) {
235 var errors = [];
236 var details = [];
237 if (guards_1.isAPIResponseError(res.body) && typeof res.body.error.details !== 'undefined') {
238 details = res.body.error.details;
239 }
240 for (var i = 0; i < details.length; i++) {
241 var detail = details[i];
242 if (detail.error_type) {
243 errors.push(detail.error_type + '_' + detail.parameter);
244 }
245 }
246 return new errors_1.DetailedError('Error creating user', errors);
247 };
248 return Auth;
249}());
250exports.Auth = Auth;
251/**
252 * @hidden
253 */
254var AuthType = (function () {
255 function AuthType(deps) {
256 this.config = deps.config;
257 this.client = deps.client;
258 this.emitter = deps.emitter;
259 }
260 AuthType.prototype.parseInAppBrowserOptions = function (opts) {
261 if (!opts) {
262 return '';
263 }
264 var p = [];
265 for (var k in opts) {
266 var v = void 0;
267 if (typeof opts[k] === 'boolean') {
268 v = opts[k] ? 'yes' : 'no';
269 }
270 else {
271 v = opts[k];
272 }
273 p.push(k + "=" + v);
274 }
275 return p.join(',');
276 };
277 AuthType.prototype.inAppBrowserFlow = function (moduleId, data, options) {
278 var _this = this;
279 if (data === void 0) { data = {}; }
280 if (options === void 0) { options = {}; }
281 var deferred = new promise_1.DeferredPromise();
282 if (!window || !window.cordova) {
283 return deferred.reject(new Error('Cordova is missing--can\'t login with InAppBrowser flow.'));
284 }
285 this.emitter.once('cordova:deviceready', function () {
286 if (!window.cordova.InAppBrowser) {
287 deferred.reject(new Error('InAppBrowser plugin missing'));
288 return;
289 }
290 _this.client.post("/auth/login/" + moduleId)
291 .send({
292 'app_id': _this.config.get('app_id'),
293 'callback': window.location.href,
294 'data': data
295 })
296 .end(function (err, res) {
297 if (err) {
298 deferred.reject(err);
299 }
300 else {
301 var w_1 = window.cordova.InAppBrowser.open(res.body.data.url, '_blank', _this.parseInAppBrowserOptions(options.inAppBrowserOptions));
302 var onExit_1 = function () {
303 deferred.reject(new Error('InAppBrowser exit'));
304 };
305 var onLoadError_1 = function () {
306 deferred.reject(new Error('InAppBrowser loaderror'));
307 };
308 var onLoadStart = function (data) {
309 if (data.url.slice(0, 20) === 'http://auth.ionic.io') {
310 var queryString = data.url.split('#')[0].split('?')[1];
311 var paramParts = queryString.split('&');
312 var params = {};
313 for (var i = 0; i < paramParts.length; i++) {
314 var part = paramParts[i].split('=');
315 params[part[0]] = part[1];
316 }
317 w_1.removeEventListener('exit', onExit_1);
318 w_1.removeEventListener('loaderror', onLoadError_1);
319 w_1.close();
320 deferred.resolve({
321 'token': params['token'],
322 'signup': Boolean(parseInt(params['signup'], 10))
323 });
324 }
325 };
326 w_1.addEventListener('exit', onExit_1);
327 w_1.addEventListener('loaderror', onLoadError_1);
328 w_1.addEventListener('loadstart', onLoadStart);
329 }
330 });
331 });
332 return deferred.promise;
333 };
334 return AuthType;
335}());
336exports.AuthType = AuthType;
337/**
338 * @hidden
339 */
340var BasicAuthType = (function (_super) {
341 __extends(BasicAuthType, _super);
342 function BasicAuthType() {
343 _super.apply(this, arguments);
344 }
345 BasicAuthType.prototype.authenticate = function (data, options) {
346 var deferred = new promise_1.DeferredPromise();
347 if (!data.email || !data.password) {
348 return deferred.reject(new Error('email and password are required for basic authentication'));
349 }
350 this.client.post('/auth/login')
351 .send({
352 'app_id': this.config.get('app_id'),
353 'email': data.email,
354 'password': data.password
355 })
356 .end(function (err, res) {
357 if (err) {
358 deferred.reject(err);
359 }
360 else {
361 deferred.resolve({
362 'token': res.body.data.token
363 });
364 }
365 });
366 return deferred.promise;
367 };
368 BasicAuthType.prototype.requestPasswordReset = function (email) {
369 var deferred = new promise_1.DeferredPromise();
370 if (!email) {
371 return deferred.reject(new Error('Email is required for password reset request.'));
372 }
373 this.client.post('/users/password/reset')
374 .send({
375 'app_id': this.config.get('app_id'),
376 'email': email,
377 'flow': 'app'
378 })
379 .end(function (err, res) {
380 if (err) {
381 deferred.reject(err);
382 }
383 else {
384 deferred.resolve();
385 }
386 });
387 return deferred.promise;
388 };
389 BasicAuthType.prototype.confirmPasswordReset = function (email, code, newPassword) {
390 var deferred = new promise_1.DeferredPromise();
391 if (!code || !email || !newPassword) {
392 return deferred.reject(new Error('Code, new password, and email are required.'));
393 }
394 this.client.post('/users/password')
395 .send({
396 'reset_token': code,
397 'new_password': newPassword,
398 'email': email
399 })
400 .end(function (err, res) {
401 if (err) {
402 deferred.reject(err);
403 }
404 else {
405 deferred.resolve();
406 }
407 });
408 return deferred.promise;
409 };
410 BasicAuthType.prototype.signup = function (data) {
411 var deferred = new promise_1.DeferredPromise();
412 if (data.email) {
413 if (!util_1.isValidEmail(data.email)) {
414 return deferred.reject(new errors_1.DetailedError('Invalid email supplied.', ['invalid_email']));
415 }
416 }
417 else {
418 return deferred.reject(new errors_1.DetailedError('Email is required for email/password auth signup.', ['required_email']));
419 }
420 if (!data.password) {
421 return deferred.reject(new errors_1.DetailedError('Password is required for email/password auth signup.', ['required_password']));
422 }
423 var userData = {
424 'app_id': this.config.get('app_id'),
425 'email': data.email,
426 'password': data.password
427 };
428 // optional details
429 if (data.username) {
430 userData.username = data.username;
431 }
432 if (data.image) {
433 userData.image = data.image;
434 }
435 if (data.name) {
436 userData.name = data.name;
437 }
438 if (data.custom) {
439 userData.custom = data.custom;
440 }
441 this.client.post('/users')
442 .send(userData)
443 .end(function (err, res) {
444 if (err) {
445 deferred.reject(Auth.getDetailedErrorFromResponse(err.response));
446 }
447 else {
448 deferred.resolve();
449 }
450 });
451 return deferred.promise;
452 };
453 return BasicAuthType;
454}(AuthType));
455exports.BasicAuthType = BasicAuthType;
456/**
457 * hidden
458 */
459var NativeAuth = (function () {
460 function NativeAuth(deps) {
461 this.config = deps.config;
462 this.client = deps.client;
463 this.userService = deps.userService;
464 this.tokenContext = deps.tokenContext;
465 this.emitter = deps.emitter;
466 }
467 /**
468 * Get the raw auth token of the active user from local storage.
469 * @hidden
470 */
471 NativeAuth.prototype.getToken = function () {
472 return this.tokenContext.get();
473 };
474 /**
475 * @hidden
476 */
477 NativeAuth.prototype.storeToken = function (token) {
478 var originalToken = this.authToken;
479 this.authToken = token;
480 this.tokenContext.store(this.authToken, { 'permanent': true });
481 this.emitter.emit('auth:token-changed', { 'old': originalToken, 'new': this.authToken });
482 };
483 return NativeAuth;
484}());
485exports.NativeAuth = NativeAuth;
486/**
487 * GoogleNativeAuth handles logging into googleplus through the cordova-plugin-googleplus plugin.'
488 * @featured
489 */
490var GoogleAuth = (function (_super) {
491 __extends(GoogleAuth, _super);
492 function GoogleAuth() {
493 _super.apply(this, arguments);
494 }
495 GoogleAuth.prototype.logout = function () {
496 var deferred = new promise_1.DeferredPromise();
497 this.tokenContext.delete();
498 var user = this.userService.current();
499 user.unstore();
500 user.clear();
501 ionic_native_1.GooglePlus.logout().then(function () {
502 deferred.resolve();
503 }, function (err) {
504 deferred.reject(err);
505 });
506 return deferred.promise;
507 };
508 GoogleAuth.prototype.login = function () {
509 var _this = this;
510 var deferred = new promise_1.DeferredPromise();
511 var authConfig = this.config.settings.auth;
512 this.emitter.once('cordova:deviceready', function () {
513 var scope = ['profile', 'email'];
514 if (!ionic_native_1.GooglePlus) {
515 deferred.reject(new Error('Ionic native is not installed'));
516 return;
517 }
518 if (!window || !window.cordova) {
519 deferred.reject(new Error('Cordova is missing'));
520 return;
521 }
522 if (!window.plugins || !window.plugins.googleplus) {
523 deferred.reject(new Error('GooglePlus cordova plugin is missing.'));
524 return;
525 }
526 if (!authConfig || !authConfig.google || !authConfig.google.webClientId) {
527 deferred.reject(new Error('Missing google web client id. Please visit http://docs.ionic.io/services/users/google-auth.html#native'));
528 return;
529 }
530 if (authConfig.google.scope) {
531 authConfig.google.scope.forEach(function (item) {
532 if (scope.indexOf(item) === -1) {
533 scope.push(item);
534 }
535 });
536 }
537 ionic_native_1.GooglePlus.login({ 'webClientId': authConfig.google.webClientId, 'offline': true, 'scopes': scope.join(' ') }).then(function (success) {
538 if (!success.serverAuthCode) {
539 deferred.reject(new Error('Failed to retrieve offline access token.'));
540 return;
541 }
542 var request_object = {
543 'app_id': _this.config.get('app_id'),
544 'serverAuthCode': success.serverAuthCode,
545 'additional_fields': scope,
546 'flow': 'native-mobile'
547 };
548 _this.client.post('/auth/login/google')
549 .send(request_object)
550 .end(function (err, res) {
551 if (err) {
552 deferred.reject(err);
553 }
554 else {
555 _this.storeToken(res.body.data.token);
556 _this.userService.load().then(function () {
557 var user = _this.userService.current();
558 user.store();
559 deferred.resolve({
560 'token': res.body.data.token,
561 'signup': Boolean(parseInt(res.body.data.signup, 10))
562 });
563 });
564 }
565 });
566 }, function (err) {
567 deferred.reject(err);
568 });
569 });
570 return deferred.promise;
571 };
572 return GoogleAuth;
573}(NativeAuth));
574exports.GoogleAuth = GoogleAuth;
575/**
576 * FacebookNative handles logging into facebook through the cordova-plugin-facebook4 plugin.
577 * @featured
578 */
579var FacebookAuth = (function (_super) {
580 __extends(FacebookAuth, _super);
581 function FacebookAuth() {
582 _super.apply(this, arguments);
583 }
584 FacebookAuth.prototype.logout = function () {
585 var deferred = new promise_1.DeferredPromise();
586 this.tokenContext.delete();
587 var user = this.userService.current();
588 user.unstore();
589 user.clear();
590 // Clear the facebook auth.
591 ionic_native_1.Facebook.logout().then(function () {
592 deferred.resolve();
593 }, function (err) {
594 deferred.reject(err);
595 });
596 return deferred.promise;
597 };
598 FacebookAuth.prototype.login = function () {
599 var _this = this;
600 var deferred = new promise_1.DeferredPromise();
601 var authConfig = this.config.settings.auth;
602 var scope = ['public_profile', 'email'];
603 if (authConfig && authConfig.facebook && authConfig.facebook.scope) {
604 authConfig.facebook.scope.forEach(function (item) {
605 if (scope.indexOf(item) === -1) {
606 scope.push(item);
607 }
608 });
609 }
610 this.emitter.once('cordova:deviceready', function () {
611 if (!ionic_native_1.Facebook) {
612 deferred.reject(new Error('Ionic native is not installed'));
613 return;
614 }
615 if (!window || !window.cordova) {
616 deferred.reject(new Error('Cordova is missing.'));
617 return;
618 }
619 if (!window.facebookConnectPlugin) {
620 deferred.reject(new Error('Please install the cordova-plugin-facebook4 plugin'));
621 return;
622 }
623 ionic_native_1.Facebook.login(scope).then(function (r) {
624 scope.splice(scope.indexOf('public_profile'), 1);
625 var request_object = {
626 'app_id': _this.config.get('app_id'),
627 'access_token': r.authResponse.accessToken,
628 'additional_fields': scope,
629 'flow': 'native-mobile'
630 };
631 _this.client.post('/auth/login/facebook')
632 .send(request_object)
633 .end(function (err, res) {
634 if (err) {
635 deferred.reject(err);
636 }
637 else {
638 _this.storeToken(res.body.data.token);
639 _this.userService.load().then(function () {
640 var user = _this.userService.current();
641 user.store();
642 deferred.resolve({
643 'token': res.body.data.token,
644 'signup': Boolean(parseInt(res.body.data.signup, 10))
645 });
646 });
647 }
648 });
649 }, function (err) {
650 deferred.reject(err);
651 });
652 });
653 return deferred.promise;
654 };
655 return FacebookAuth;
656}(NativeAuth));
657exports.FacebookAuth = FacebookAuth;
658/**
659 * @hidden
660 */
661var CustomAuthType = (function (_super) {
662 __extends(CustomAuthType, _super);
663 function CustomAuthType() {
664 _super.apply(this, arguments);
665 }
666 CustomAuthType.prototype.authenticate = function (data, options) {
667 if (data === void 0) { data = {}; }
668 return this.inAppBrowserFlow('custom', data, options);
669 };
670 return CustomAuthType;
671}(AuthType));
672exports.CustomAuthType = CustomAuthType;
673/**
674 * @hidden
675 */
676var TwitterAuthType = (function (_super) {
677 __extends(TwitterAuthType, _super);
678 function TwitterAuthType() {
679 _super.apply(this, arguments);
680 }
681 TwitterAuthType.prototype.authenticate = function (data, options) {
682 if (data === void 0) { data = {}; }
683 return this.inAppBrowserFlow('twitter', data, options);
684 };
685 return TwitterAuthType;
686}(AuthType));
687exports.TwitterAuthType = TwitterAuthType;
688/**
689 * @hidden
690 */
691var FacebookAuthType = (function (_super) {
692 __extends(FacebookAuthType, _super);
693 function FacebookAuthType() {
694 _super.apply(this, arguments);
695 }
696 FacebookAuthType.prototype.authenticate = function (data, options) {
697 if (data === void 0) { data = {}; }
698 return this.inAppBrowserFlow('facebook', data, options);
699 };
700 return FacebookAuthType;
701}(AuthType));
702exports.FacebookAuthType = FacebookAuthType;
703/**
704 * @hidden
705 */
706var GithubAuthType = (function (_super) {
707 __extends(GithubAuthType, _super);
708 function GithubAuthType() {
709 _super.apply(this, arguments);
710 }
711 GithubAuthType.prototype.authenticate = function (data, options) {
712 if (data === void 0) { data = {}; }
713 return this.inAppBrowserFlow('github', data, options);
714 };
715 return GithubAuthType;
716}(AuthType));
717exports.GithubAuthType = GithubAuthType;
718/**
719 * @hidden
720 */
721var GoogleAuthType = (function (_super) {
722 __extends(GoogleAuthType, _super);
723 function GoogleAuthType() {
724 _super.apply(this, arguments);
725 }
726 GoogleAuthType.prototype.authenticate = function (data, options) {
727 if (data === void 0) { data = {}; }
728 return this.inAppBrowserFlow('google', data, options);
729 };
730 return GoogleAuthType;
731}(AuthType));
732exports.GoogleAuthType = GoogleAuthType;
733/**
734 * @hidden
735 */
736var InstagramAuthType = (function (_super) {
737 __extends(InstagramAuthType, _super);
738 function InstagramAuthType() {
739 _super.apply(this, arguments);
740 }
741 InstagramAuthType.prototype.authenticate = function (data, options) {
742 if (data === void 0) { data = {}; }
743 return this.inAppBrowserFlow('instagram', data, options);
744 };
745 return InstagramAuthType;
746}(AuthType));
747exports.InstagramAuthType = InstagramAuthType;
748/**
749 * @hidden
750 */
751var LinkedInAuthType = (function (_super) {
752 __extends(LinkedInAuthType, _super);
753 function LinkedInAuthType() {
754 _super.apply(this, arguments);
755 }
756 LinkedInAuthType.prototype.authenticate = function (data, options) {
757 if (data === void 0) { data = {}; }
758 return this.inAppBrowserFlow('linkedin', data, options);
759 };
760 return LinkedInAuthType;
761}(AuthType));
762exports.LinkedInAuthType = LinkedInAuthType;
763//# sourceMappingURL=auth.js.map
\No newline at end of file