UNPKG

auth0-js

Version:

Auth0 headless browser sdk

342 lines (304 loc) 11.5 kB
import urljoin from 'url-join'; import WinChan from 'winchan'; import urlHelper from '../helper/url'; import assert from '../helper/assert'; import responseHandler from '../helper/response-handler'; import PopupHandler from '../helper/popup-handler'; import objectHelper from '../helper/object'; import windowHelper from '../helper/window'; import Warn from '../helper/warn'; import TransactionManager from './transaction-manager'; import CrossOriginAuthentication from './cross-origin-authentication'; /** * @class * @classdesc This class cannot be instantiated directly. Instead, use WebAuth.popup * @hideconstructor */ function Popup(webAuth, options) { this.baseOptions = options; this.baseOptions.popupOrigin = options.popupOrigin; this.client = webAuth.client; this.webAuth = webAuth; this.transactionManager = new TransactionManager(this.baseOptions); this.crossOriginAuthentication = new CrossOriginAuthentication( webAuth, this.baseOptions ); this.warn = new Warn({ disableWarnings: !!options._disableDeprecationWarnings }); } /** * Returns a new instance of the popup handler * * @method buildPopupHandler * @private */ Popup.prototype.buildPopupHandler = function() { var pluginHandler = this.baseOptions.plugins.get('popup.getPopupHandler'); if (pluginHandler) { return pluginHandler.getPopupHandler(); } return new PopupHandler(); }; /** * Initializes the popup window and returns the instance to be used later in order to avoid being blocked by the browser. * * @method preload * @param {Object} options receives the window height and width and any other window feature to be sent to window.open * @memberof Popup.prototype */ Popup.prototype.preload = function(options) { options = options || {}; var popup = this.buildPopupHandler(); popup.preload(options); return popup; }; /** * Internal use. * * @method getPopupHandler * @private */ Popup.prototype.getPopupHandler = function(options, preload) { if (options.popupHandler) { return options.popupHandler; } if (preload) { return this.preload(options); } return this.buildPopupHandler(); }; /** * Handles the popup logic for the callback page. * * @method callback * @param {Object} options * @param {String} options.hash the url hash. If not provided it will extract from window.location.hash * @param {String} [options.state] value originally sent in `state` parameter to {@link authorize} to mitigate XSRF * @param {String} [options.nonce] value originally sent in `nonce` parameter to {@link authorize} to prevent replay attacks * @see {@link parseHash} * @memberof Popup.prototype */ Popup.prototype.callback = function(options) { var _this = this; var theWindow = windowHelper.getWindow(); options = options || {}; var originUrl = options.popupOrigin || this.baseOptions.popupOrigin || windowHelper.getOrigin(); /* in IE 11, there's a bug that makes window.opener return undefined. The callback page will still call `popup.callback()` which will run this method in the relay page. WinChan expects the relay page to have a global `doPost` function, which will be called with the response. IE11 Bug: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/110920/ */ if (!theWindow.opener) { theWindow.doPost = function(msg) { if (theWindow.parent) { theWindow.parent.postMessage(msg, originUrl); } }; return; } WinChan.onOpen(function(popupOrigin, r, cb) { if (popupOrigin !== originUrl) { return cb({ error: 'origin_mismatch', error_description: "The popup's origin (" + popupOrigin + ') should match the `popupOrigin` parameter (' + originUrl + ').' }); } _this.webAuth.parseHash(options || {}, function(err, data) { return cb(err || data); }); }); }; /** * Shows inside a new window the hosted login page (`/authorize`) in order to start a new authN/authZ transaction and post its result using `postMessage`. * * @method authorize * @param {Object} options * @param {String} [options.clientID] the Client ID found on your Application settings page * @param {String} options.redirectUri url that the Auth0 will redirect after Auth with the Authorization Response * @param {String} options.responseType type of the response used by OAuth 2.0 flow. It can be any space separated list of the values `code`, `token`, `id_token`. {@link https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html} * @param {String} [options.responseMode] how the Auth response is encoded and redirected back to the client. Supported values are `query`, `fragment` and `form_post`. The `query` value is only supported when `responseType` is `code`. {@link https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes} * @param {String} [options.state] value used to mitigate XSRF attacks. {@link https://auth0.com/docs/protocols/oauth2/oauth-state} * @param {String} [options.nonce] value used to mitigate replay attacks when using Implicit Grant. {@link https://auth0.com/docs/api-auth/tutorials/nonce} * @param {String} [options.scope] scopes to be requested during Auth. e.g. `openid email` * @param {String} [options.audience] identifier of the resource server who will consume the access token issued after Auth * @param {String} [options.organization] the Id of an organization to log in to * @param {String} [options.invitation] the ID of an invitation to accept. This is available from the user invitation URL that is given when participating in a user invitation flow * @param {Boolean} [options.owp] determines if Auth0 should render the relay page or not and the caller is responsible of handling the response. * @param {authorizeCallback} cb * @see {@link https://auth0.com/docs/api/authentication#authorize-client} * @memberof Popup.prototype */ Popup.prototype.authorize = function(options, cb) { var popup; var url; var relayUrl; var popOpts = {}; var pluginHandler = this.baseOptions.plugins.get('popup.authorize'); var params = objectHelper .merge(this.baseOptions, [ 'clientID', 'scope', 'domain', 'audience', 'tenant', 'responseType', 'redirectUri', '_csrf', 'state', '_intstate', 'nonce', 'organization', 'invitation' ]) .with(objectHelper.blacklist(options, ['popupHandler'])); assert.check( params, { type: 'object', message: 'options parameter is not valid' }, { responseType: { type: 'string', message: 'responseType option is required' } } ); // the relay page should not be necessary as long it happens in the same domain // (a redirectUri shoul be provided). It is necessary when using OWP relayUrl = urljoin(this.baseOptions.rootUrl, 'relay.html'); // if a owp is enabled, it should use the owp flag if (options.owp) { // used by server to render the relay page instead of sending the chunk in the // url to the callback params.owp = true; } else { popOpts.origin = urlHelper.extractOrigin(params.redirectUri); relayUrl = params.redirectUri; } if (options.popupOptions) { popOpts.popupOptions = objectHelper.pick(options.popupOptions, [ 'width', 'height', 'top', 'left' ]); } if (pluginHandler) { params = pluginHandler.processParams(params); } params = this.transactionManager.process(params); params.scope = params.scope || 'openid profile email'; delete params.domain; url = this.client.buildAuthorizeUrl(params); popup = this.getPopupHandler(options); return popup.load( url, relayUrl, popOpts, responseHandler(cb, { keepOriginalCasing: true }) ); }; /** * Performs authentication with username/email and password with a database connection inside a new window * * This method is not compatible with API Auth so if you need to fetch API tokens with audience * you should use {@link authorize} or {@link login}. * * @method loginWithCredentials * @param {Object} options * @param {String} [options.redirectUri] url that the Auth0 will redirect after Auth with the Authorization Response * @param {String} [options.responseType] type of the response used. It can be any of the values `code` and `token` * @param {String} [options.responseMode] how the AuthN response is encoded and redirected back to the client. Supported values are `query` and `fragment`. The `query` value is only supported when `responseType` is `code`. * @param {String} [options.scope] scopes to be requested during AuthN. e.g. `openid email` * @param {credentialsCallback} cb * @memberof Popup.prototype */ Popup.prototype.loginWithCredentials = function (options, cb) { options.realm = options.realm || options.connection; options.popup = true; options = objectHelper .merge(this.baseOptions, [ 'redirectUri', 'responseType', 'state', 'nonce', 'timeout' ]) .with(objectHelper.blacklist(options, ['popupHandler', 'connection'])); options = this.transactionManager.process(options); this.crossOriginAuthentication.login(options, cb); }; /** * Verifies the passwordless TOTP and redirects to finish the passwordless transaction * * @method passwordlessVerify * @param {Object} options * @param {String} options.type `sms` or `email` * @param {String} options.phoneNumber only if type = sms * @param {String} options.email only if type = email * @param {String} options.connection the connection name * @param {String} options.verificationCode the TOTP code * @param {Function} cb * @memberof Popup.prototype */ Popup.prototype.passwordlessVerify = function(options, cb) { var _this = this; return this.client.passwordless.verify( objectHelper.blacklist(options, ['popupHandler']), function(err) { if (err) { return cb(err); } options.username = options.phoneNumber || options.email; options.password = options.verificationCode; delete options.email; delete options.phoneNumber; delete options.verificationCode; delete options.type; _this.client.loginWithResourceOwner(options, cb); } ); }; /** * Signs up a new user and automatically logs the user in after the signup. * * This method is not compatible with API Auth so if you need to fetch API tokens with audience * you should use {@link authorize} or {@link signupAndAuthorize}. * * @method signupAndLogin * @param {Object} options * @param {String} options.email user email address * @param {String} options.password user password * @param {String} options.connection name of the connection where the user will be created * @param {credentialsCallback} cb * @memberof Popup.prototype */ Popup.prototype.signupAndLogin = function(options, cb) { var _this = this; return this.client.dbConnection.signup(options, function(err) { if (err) { return cb(err); } _this.loginWithCredentials(options, function(loginErr, result) { if (loginErr) { loginErr.errorInfo = { error_code: 'login_error', message: 'Your account was created successfully, but we could not log you in automatically. Please try logging in with your new credentials.' }; return cb(loginErr); } return cb(null, result); }); }); }; export default Popup;