UNPKG

@esri/arcgis-rest-auth

Version:

Authentication helpers for @esri/arcgis-rest-js.

911 lines 39.7 kB
"use strict"; /* Copyright (c) 2017-2019 Environmental Systems Research Institute, Inc. * Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.UserSession = void 0; var tslib_1 = require("tslib"); var arcgis_rest_request_1 = require("@esri/arcgis-rest-request"); var generate_token_1 = require("./generate-token"); var fetch_token_1 = require("./fetch-token"); var federation_utils_1 = require("./federation-utils"); var validate_app_access_1 = require("./validate-app-access"); function defer() { var deferred = { promise: null, resolve: null, reject: null, }; deferred.promise = new Promise(function (resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }); return deferred; } /** * ```js * import { UserSession } from '@esri/arcgis-rest-auth'; * UserSession.beginOAuth2({ * // register an app of your own to create a unique clientId * clientId: "abc123", * redirectUri: 'https://yourapp.com/authenticate.html' * }) * .then(session) * // or * new UserSession({ * username: "jsmith", * password: "123456" * }) * // or * UserSession.deserialize(cache) * ``` * Used to authenticate both ArcGIS Online and ArcGIS Enterprise users. `UserSession` includes helper methods for [OAuth 2.0](/arcgis-rest-js/guides/browser-authentication/) in both browser and server applications. */ var UserSession = /** @class */ (function () { function UserSession(options) { this.clientId = options.clientId; this._refreshToken = options.refreshToken; this._refreshTokenExpires = options.refreshTokenExpires; this.username = options.username; this.password = options.password; this._token = options.token; this._tokenExpires = options.tokenExpires; this.portal = options.portal ? arcgis_rest_request_1.cleanUrl(options.portal) : "https://www.arcgis.com/sharing/rest"; this.ssl = options.ssl; this.provider = options.provider || "arcgis"; this.tokenDuration = options.tokenDuration || 20160; this.redirectUri = options.redirectUri; this.refreshTokenTTL = options.refreshTokenTTL || 20160; this.server = options.server; this.federatedServers = {}; this.trustedDomains = []; // if a non-federated server was passed explicitly, it should be trusted. if (options.server) { // if the url includes more than '/arcgis/', trim the rest var root = this.getServerRootUrl(options.server); this.federatedServers[root] = { token: options.token, expires: options.tokenExpires, }; } this._pendingTokenRequests = {}; } Object.defineProperty(UserSession.prototype, "token", { /** * The current ArcGIS Online or ArcGIS Enterprise `token`. */ get: function () { return this._token; }, enumerable: false, configurable: true }); Object.defineProperty(UserSession.prototype, "tokenExpires", { /** * The expiration time of the current `token`. */ get: function () { return this._tokenExpires; }, enumerable: false, configurable: true }); Object.defineProperty(UserSession.prototype, "refreshToken", { /** * The current token to ArcGIS Online or ArcGIS Enterprise. */ get: function () { return this._refreshToken; }, enumerable: false, configurable: true }); Object.defineProperty(UserSession.prototype, "refreshTokenExpires", { /** * The expiration time of the current `refreshToken`. */ get: function () { return this._refreshTokenExpires; }, enumerable: false, configurable: true }); Object.defineProperty(UserSession.prototype, "trustedServers", { /** * Deprecated, use `federatedServers` instead. * * @deprecated */ get: function () { console.log("DEPRECATED: use federatedServers instead"); return this.federatedServers; }, enumerable: false, configurable: true }); /** * Begins a new browser-based OAuth 2.0 sign in. If `options.popup` is `true` the * authentication window will open in a new tab/window and the function will return * Promise&lt;UserSession&gt;. Otherwise, the user will be redirected to the * authorization page in their current tab/window and the function will return `undefined`. * * @browserOnly */ /* istanbul ignore next */ UserSession.beginOAuth2 = function (options, win) { if (win === void 0) { win = window; } if (options.duration) { console.log("DEPRECATED: 'duration' is deprecated - use 'expiration' instead"); } var _a = tslib_1.__assign({ portal: "https://www.arcgis.com/sharing/rest", provider: "arcgis", expiration: 20160, popup: true, popupWindowFeatures: "height=400,width=600,menubar=no,location=yes,resizable=yes,scrollbars=yes,status=yes", state: options.clientId, locale: "", }, options), portal = _a.portal, provider = _a.provider, clientId = _a.clientId, expiration = _a.expiration, redirectUri = _a.redirectUri, popup = _a.popup, popupWindowFeatures = _a.popupWindowFeatures, state = _a.state, locale = _a.locale, params = _a.params; var url; if (provider === "arcgis") { url = portal + "/oauth2/authorize?client_id=" + clientId + "&response_type=token&expiration=" + (options.duration || expiration) + "&redirect_uri=" + encodeURIComponent(redirectUri) + "&state=" + state + "&locale=" + locale; } else { url = portal + "/oauth2/social/authorize?client_id=" + clientId + "&socialLoginProviderName=" + provider + "&autoAccountCreateForSocial=true&response_type=token&expiration=" + (options.duration || expiration) + "&redirect_uri=" + encodeURIComponent(redirectUri) + "&state=" + state + "&locale=" + locale; } // append additional params if (params) { url = url + "&" + arcgis_rest_request_1.encodeQueryString(params); } if (!popup) { win.location.href = url; return undefined; } var session = defer(); win["__ESRI_REST_AUTH_HANDLER_" + clientId] = function (errorString, oauthInfoString) { if (errorString) { var error = JSON.parse(errorString); session.reject(new arcgis_rest_request_1.ArcGISAuthError(error.errorMessage, error.error)); return; } if (oauthInfoString) { var oauthInfo = JSON.parse(oauthInfoString); session.resolve(new UserSession({ clientId: clientId, portal: portal, ssl: oauthInfo.ssl, token: oauthInfo.token, tokenExpires: new Date(oauthInfo.expires), username: oauthInfo.username, })); } }; win.open(url, "oauth-window", popupWindowFeatures); return session.promise; }; /** * Completes a browser-based OAuth 2.0 sign in. If `options.popup` is `true` the user * will be returned to the previous window. Otherwise a new `UserSession` * will be returned. You must pass the same values for `options.popup` and * `options.portal` as you used in `beginOAuth2()`. * * @browserOnly */ /* istanbul ignore next */ UserSession.completeOAuth2 = function (options, win) { if (win === void 0) { win = window; } var _a = tslib_1.__assign({ portal: "https://www.arcgis.com/sharing/rest", popup: true }, options), portal = _a.portal, clientId = _a.clientId, popup = _a.popup; function completeSignIn(error, oauthInfo) { try { var handlerFn = void 0; var handlerFnName = "__ESRI_REST_AUTH_HANDLER_" + clientId; if (popup) { // Guard b/c IE does not support window.opener if (win.opener) { if (win.opener.parent && win.opener.parent[handlerFnName]) { handlerFn = win.opener.parent[handlerFnName]; } else if (win.opener && win.opener[handlerFnName]) { // support pop-out oauth from within an iframe handlerFn = win.opener[handlerFnName]; } } else { // IE if (win !== win.parent && win.parent && win.parent[handlerFnName]) { handlerFn = win.parent[handlerFnName]; } } // if we have a handler fn, call it and close the window if (handlerFn) { handlerFn(error ? JSON.stringify(error) : undefined, JSON.stringify(oauthInfo)); win.close(); return undefined; } } } catch (e) { throw new arcgis_rest_request_1.ArcGISAuthError("Unable to complete authentication. It's possible you specified popup based oAuth2 but no handler from \"beginOAuth2()\" present. This generally happens because the \"popup\" option differs between \"beginOAuth2()\" and \"completeOAuth2()\"."); } if (error) { throw new arcgis_rest_request_1.ArcGISAuthError(error.errorMessage, error.error); } return new UserSession({ clientId: clientId, portal: portal, ssl: oauthInfo.ssl, token: oauthInfo.token, tokenExpires: oauthInfo.expires, username: oauthInfo.username, }); } var params = arcgis_rest_request_1.decodeQueryString(win.location.hash); if (!params.access_token) { var error = void 0; var errorMessage = "Unknown error"; if (params.error) { error = params.error; errorMessage = params.error_description; } return completeSignIn({ error: error, errorMessage: errorMessage }); } var token = params.access_token; var expires = new Date(Date.now() + parseInt(params.expires_in, 10) * 1000 - 60 * 1000); var username = params.username; var ssl = params.ssl === "true"; return completeSignIn(undefined, { token: token, expires: expires, ssl: ssl, username: username, }); }; /** * Request session information from the parent application * * When an application is embedded into another application via an IFrame, the embedded app can * use `window.postMessage` to request credentials from the host application. This function wraps * that behavior. * * The ArcGIS API for Javascript has this built into the Identity Manager as of the 4.19 release. * * Note: The parent application will not respond if the embedded app's origin is not: * - the same origin as the parent or *.arcgis.com (JSAPI) * - in the list of valid child origins (REST-JS) * * * @param parentOrigin origin of the parent frame. Passed into the embedded application as `parentOrigin` query param * @browserOnly */ UserSession.fromParent = function (parentOrigin, win) { /* istanbul ignore next: must pass in a mockwindow for tests so we can't cover the other branch */ if (!win && window) { win = window; } // Declare handler outside of promise scope so we can detach it var handler; // return a promise that will resolve when the handler receives // session information from the correct origin return new Promise(function (resolve, reject) { // create an event handler that just wraps the parentMessageHandler handler = function (event) { // ensure we only listen to events from the parent if (event.source === win.parent && event.data) { try { return resolve(UserSession.parentMessageHandler(event)); } catch (err) { return reject(err); } } }; // add listener win.addEventListener("message", handler, false); win.parent.postMessage({ type: "arcgis:auth:requestCredential" }, parentOrigin); }).then(function (session) { win.removeEventListener("message", handler, false); return session; }); }; /** * Begins a new server-based OAuth 2.0 sign in. This will redirect the user to * the ArcGIS Online or ArcGIS Enterprise authorization page. * * @nodeOnly */ UserSession.authorize = function (options, response) { if (options.duration) { console.log("DEPRECATED: 'duration' is deprecated - use 'expiration' instead"); } var _a = tslib_1.__assign({ portal: "https://arcgis.com/sharing/rest", expiration: 20160 }, options), portal = _a.portal, clientId = _a.clientId, expiration = _a.expiration, redirectUri = _a.redirectUri; response.writeHead(301, { Location: portal + "/oauth2/authorize?client_id=" + clientId + "&expiration=" + (options.duration || expiration) + "&response_type=code&redirect_uri=" + encodeURIComponent(redirectUri), }); response.end(); }; /** * Completes the server-based OAuth 2.0 sign in process by exchanging the `authorizationCode` * for a `access_token`. * * @nodeOnly */ UserSession.exchangeAuthorizationCode = function (options, authorizationCode) { var _a = tslib_1.__assign({ portal: "https://www.arcgis.com/sharing/rest", refreshTokenTTL: 20160, }, options), portal = _a.portal, clientId = _a.clientId, redirectUri = _a.redirectUri, refreshTokenTTL = _a.refreshTokenTTL; return fetch_token_1.fetchToken(portal + "/oauth2/token", { params: { grant_type: "authorization_code", client_id: clientId, redirect_uri: redirectUri, code: authorizationCode, }, }).then(function (response) { return new UserSession({ clientId: clientId, portal: portal, ssl: response.ssl, redirectUri: redirectUri, refreshToken: response.refreshToken, refreshTokenTTL: refreshTokenTTL, refreshTokenExpires: new Date(Date.now() + (refreshTokenTTL - 1) * 60 * 1000), token: response.token, tokenExpires: response.expires, username: response.username, }); }); }; UserSession.deserialize = function (str) { var options = JSON.parse(str); return new UserSession({ clientId: options.clientId, refreshToken: options.refreshToken, refreshTokenExpires: new Date(options.refreshTokenExpires), username: options.username, password: options.password, token: options.token, tokenExpires: new Date(options.tokenExpires), portal: options.portal, ssl: options.ssl, tokenDuration: options.tokenDuration, redirectUri: options.redirectUri, refreshTokenTTL: options.refreshTokenTTL, }); }; /** * Translates authentication from the format used in the [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/). * * ```js * UserSession.fromCredential({ * userId: "jsmith", * token: "secret" * }); * ``` * * @returns UserSession */ UserSession.fromCredential = function (credential) { // At ArcGIS Online 9.1, credentials no longer include the ssl and expires properties // Here, we provide default values for them to cover this condition var ssl = typeof credential.ssl !== "undefined" ? credential.ssl : true; var expires = credential.expires || Date.now() + 7200000; /* 2 hours */ return new UserSession({ portal: credential.server.includes("sharing/rest") ? credential.server : credential.server + "/sharing/rest", ssl: ssl, token: credential.token, username: credential.userId, tokenExpires: new Date(expires), }); }; /** * Handle the response from the parent * @param event DOM Event */ UserSession.parentMessageHandler = function (event) { if (event.data.type === "arcgis:auth:credential") { return UserSession.fromCredential(event.data.credential); } if (event.data.type === "arcgis:auth:error") { var err = new Error(event.data.error.message); err.name = event.data.error.name; throw err; } else { throw new Error("Unknown message type."); } }; /** * Returns authentication in a format useable in the [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/). * * ```js * esriId.registerToken(session.toCredential()); * ``` * * @returns ICredential */ UserSession.prototype.toCredential = function () { return { expires: this.tokenExpires.getTime(), server: this.portal, ssl: this.ssl, token: this.token, userId: this.username, }; }; /** * Returns information about the currently logged in [user](https://developers.arcgis.com/rest/users-groups-and-items/user.htm). Subsequent calls will *not* result in additional web traffic. * * ```js * session.getUser() * .then(response => { * console.log(response.role); // "org_admin" * }) * ``` * * @param requestOptions - Options for the request. NOTE: `rawResponse` is not supported by this operation. * @returns A Promise that will resolve with the data from the response. */ UserSession.prototype.getUser = function (requestOptions) { var _this = this; if (this._pendingUserRequest) { return this._pendingUserRequest; } else if (this._user) { return Promise.resolve(this._user); } else { var url = this.portal + "/community/self"; var options = tslib_1.__assign(tslib_1.__assign({ httpMethod: "GET", authentication: this }, requestOptions), { rawResponse: false }); this._pendingUserRequest = arcgis_rest_request_1.request(url, options).then(function (response) { _this._user = response; _this._pendingUserRequest = null; return response; }); return this._pendingUserRequest; } }; /** * Returns information about the currently logged in user's [portal](https://developers.arcgis.com/rest/users-groups-and-items/portal-self.htm). Subsequent calls will *not* result in additional web traffic. * * ```js * session.getPortal() * .then(response => { * console.log(portal.name); // "City of ..." * }) * ``` * * @param requestOptions - Options for the request. NOTE: `rawResponse` is not supported by this operation. * @returns A Promise that will resolve with the data from the response. */ UserSession.prototype.getPortal = function (requestOptions) { var _this = this; if (this._pendingPortalRequest) { return this._pendingPortalRequest; } else if (this._portalInfo) { return Promise.resolve(this._portalInfo); } else { var url = this.portal + "/portals/self"; var options = tslib_1.__assign(tslib_1.__assign({ httpMethod: "GET", authentication: this }, requestOptions), { rawResponse: false }); this._pendingPortalRequest = arcgis_rest_request_1.request(url, options).then(function (response) { _this._portalInfo = response; _this._pendingPortalRequest = null; return response; }); return this._pendingPortalRequest; } }; /** * Returns the username for the currently logged in [user](https://developers.arcgis.com/rest/users-groups-and-items/user.htm). Subsequent calls will *not* result in additional web traffic. This is also used internally when a username is required for some requests but is not present in the options. * * * ```js * session.getUsername() * .then(response => { * console.log(response); // "casey_jones" * }) * ``` */ UserSession.prototype.getUsername = function () { if (this.username) { return Promise.resolve(this.username); } else if (this._user) { return Promise.resolve(this._user.username); } else { return this.getUser().then(function (user) { return user.username; }); } }; /** * Gets an appropriate token for the given URL. If `portal` is ArcGIS Online and * the request is to an ArcGIS Online domain `token` will be used. If the request * is to the current `portal` the current `token` will also be used. However if * the request is to an unknown server we will validate the server with a request * to our current `portal`. */ UserSession.prototype.getToken = function (url, requestOptions) { if (federation_utils_1.canUseOnlineToken(this.portal, url)) { return this.getFreshToken(requestOptions); } else if (new RegExp(this.portal, "i").test(url)) { return this.getFreshToken(requestOptions); } else { return this.getTokenForServer(url, requestOptions); } }; /** * Get application access information for the current user * see `validateAppAccess` function for details * * @param clientId application client id */ UserSession.prototype.validateAppAccess = function (clientId) { return this.getToken(this.portal).then(function (token) { return validate_app_access_1.validateAppAccess(token, clientId); }); }; UserSession.prototype.toJSON = function () { return { clientId: this.clientId, refreshToken: this.refreshToken, refreshTokenExpires: this.refreshTokenExpires, username: this.username, password: this.password, token: this.token, tokenExpires: this.tokenExpires, portal: this.portal, ssl: this.ssl, tokenDuration: this.tokenDuration, redirectUri: this.redirectUri, refreshTokenTTL: this.refreshTokenTTL, }; }; UserSession.prototype.serialize = function () { return JSON.stringify(this); }; /** * For a "Host" app that embeds other platform apps via iframes, after authenticating the user * and creating a UserSession, the app can then enable "post message" style authentication by calling * this method. * * Internally this adds an event listener on window for the `message` event * * @param validChildOrigins Array of origins that are allowed to request authentication from the host app */ UserSession.prototype.enablePostMessageAuth = function (validChildOrigins, win) { /* istanbul ignore next: must pass in a mockwindow for tests so we can't cover the other branch */ if (!win && window) { win = window; } this._hostHandler = this.createPostMessageHandler(validChildOrigins); win.addEventListener("message", this._hostHandler, false); }; /** * For a "Host" app that has embedded other platform apps via iframes, when the host needs * to transition routes, it should call `UserSession.disablePostMessageAuth()` to remove * the event listener and prevent memory leaks */ UserSession.prototype.disablePostMessageAuth = function (win) { /* istanbul ignore next: must pass in a mockwindow for tests so we can't cover the other branch */ if (!win && window) { win = window; } win.removeEventListener("message", this._hostHandler, false); }; /** * Manually refreshes the current `token` and `tokenExpires`. */ UserSession.prototype.refreshSession = function (requestOptions) { // make sure subsequent calls to getUser() don't returned cached metadata this._user = null; if (this.username && this.password) { return this.refreshWithUsernameAndPassword(requestOptions); } if (this.clientId && this.refreshToken) { return this.refreshWithRefreshToken(); } return Promise.reject(new arcgis_rest_request_1.ArcGISAuthError("Unable to refresh token.")); }; /** * Determines the root of the ArcGIS Server or Portal for a given URL. * * @param url the URl to determine the root url for. */ UserSession.prototype.getServerRootUrl = function (url) { var root = arcgis_rest_request_1.cleanUrl(url).split(/\/rest(\/admin)?\/services(?:\/|#|\?|$)/)[0]; var _a = root.match(/(https?:\/\/)(.+)/), match = _a[0], protocol = _a[1], domainAndPath = _a[2]; var _b = domainAndPath.split("/"), domain = _b[0], path = _b.slice(1); // only the domain is lowercased because in some cases an org id might be // in the path which cannot be lowercased. return "" + protocol + domain.toLowerCase() + "/" + path.join("/"); }; /** * Returns the proper [`credentials`] option for `fetch` for a given domain. * See [trusted server](https://enterprise.arcgis.com/en/portal/latest/administer/windows/configure-security.htm#ESRI_SECTION1_70CC159B3540440AB325BE5D89DBE94A). * Used internally by underlying request methods to add support for specific security considerations. * * @param url The url of the request * @returns "include" or "same-origin" */ UserSession.prototype.getDomainCredentials = function (url) { // if the url is in the noCorsDomains, we want to include credentials var shouldInclude = arcgis_rest_request_1.isNoCorsDomain(url); if (shouldInclude) { return "include"; } if (!this.trustedDomains || !this.trustedDomains.length) { return "same-origin"; } return this.trustedDomains.some(function (domainWithProtocol) { return url.startsWith(domainWithProtocol); }) ? "include" : "same-origin"; }; /** * Return a function that closes over the validOrigins array and * can be used as an event handler for the `message` event * * @param validOrigins Array of valid origins */ UserSession.prototype.createPostMessageHandler = function (validOrigins) { var _this = this; // return a function that closes over the validOrigins and // has access to the credential return function (event) { // Verify that the origin is valid // Note: do not use regex's here. validOrigins is an array so we're checking that the event's origin // is in the array via exact match. More info about avoiding postMessage xss issues here // https://jlajara.gitlab.io/web/2020/07/17/Dom_XSS_PostMessage_2.html#tipsbypasses-in-postmessage-vulnerabilities var isValidOrigin = validOrigins.indexOf(event.origin) > -1; // JSAPI handles this slightly differently - instead of checking a list, it will respond if // event.origin === window.location.origin || event.origin.endsWith('.arcgis.com') // For Hub, and to enable cross domain debugging with port's in urls, we are opting to // use a list of valid origins // Ensure the message type is something we want to handle var isValidType = event.data.type === "arcgis:auth:requestCredential"; var isTokenValid = _this.tokenExpires.getTime() > Date.now(); if (isValidOrigin && isValidType) { var msg = {}; if (isTokenValid) { var credential = _this.toCredential(); // arcgis:auth:error with {name: "", message: ""} // the following line allows us to conform to our spec without changing other depended-on functionality // https://github.com/Esri/arcgis-rest-js/blob/master/packages/arcgis-rest-auth/post-message-auth-spec.md#arcgisauthcredential credential.server = credential.server.replace("/sharing/rest", ""); msg = { type: "arcgis:auth:credential", credential: credential }; } else { // Return an error msg = { type: "arcgis:auth:error", error: { name: "tokenExpiredError", message: "Session token was expired, and not returned to the child application", }, }; } event.source.postMessage(msg, event.origin); } }; }; /** * Validates that a given URL is properly federated with our current `portal`. * Attempts to use the internal `federatedServers` cache first. */ UserSession.prototype.getTokenForServer = function (url, requestOptions) { var _this = this; // requests to /rest/services/ and /rest/admin/services/ are both valid // Federated servers may have inconsistent casing, so lowerCase it var root = this.getServerRootUrl(url); var existingToken = this.federatedServers[root]; if (existingToken && existingToken.expires && existingToken.expires.getTime() > Date.now()) { return Promise.resolve(existingToken.token); } if (this._pendingTokenRequests[root]) { return this._pendingTokenRequests[root]; } this._pendingTokenRequests[root] = this.fetchAuthorizedDomains().then(function () { return arcgis_rest_request_1.request(root + "/rest/info", { credentials: _this.getDomainCredentials(url), }) .then(function (response) { if (response.owningSystemUrl) { /** * if this server is not owned by this portal * bail out with an error since we know we wont * be able to generate a token */ if (!federation_utils_1.isFederated(response.owningSystemUrl, _this.portal)) { throw new arcgis_rest_request_1.ArcGISAuthError(url + " is not federated with " + _this.portal + ".", "NOT_FEDERATED"); } else { /** * if the server is federated, use the relevant token endpoint. */ return arcgis_rest_request_1.request(response.owningSystemUrl + "/sharing/rest/info", requestOptions); } } else if (response.authInfo && _this.federatedServers[root] !== undefined) { /** * if its a stand-alone instance of ArcGIS Server that doesn't advertise * federation, but the root server url is recognized, use its built in token endpoint. */ return Promise.resolve({ authInfo: response.authInfo, }); } else { throw new arcgis_rest_request_1.ArcGISAuthError(url + " is not federated with any portal and is not explicitly trusted.", "NOT_FEDERATED"); } }) .then(function (response) { return response.authInfo.tokenServicesUrl; }) .then(function (tokenServicesUrl) { // an expired token cant be used to generate a new token if (_this.token && _this.tokenExpires.getTime() > Date.now()) { return generate_token_1.generateToken(tokenServicesUrl, { params: { token: _this.token, serverUrl: url, expiration: _this.tokenDuration, client: "referer", }, }); // generate an entirely fresh token if necessary } else { return generate_token_1.generateToken(tokenServicesUrl, { params: { username: _this.username, password: _this.password, expiration: _this.tokenDuration, client: "referer", }, }).then(function (response) { _this._token = response.token; _this._tokenExpires = new Date(response.expires); return response; }); } }) .then(function (response) { _this.federatedServers[root] = { expires: new Date(response.expires), token: response.token, }; delete _this._pendingTokenRequests[root]; return response.token; }); }); return this._pendingTokenRequests[root]; }; /** * Returns an unexpired token for the current `portal`. */ UserSession.prototype.getFreshToken = function (requestOptions) { var _this = this; if (this.token && !this.tokenExpires) { return Promise.resolve(this.token); } if (this.token && this.tokenExpires && this.tokenExpires.getTime() > Date.now()) { return Promise.resolve(this.token); } if (!this._pendingTokenRequests[this.portal]) { this._pendingTokenRequests[this.portal] = this.refreshSession(requestOptions).then(function (session) { _this._pendingTokenRequests[_this.portal] = null; return session.token; }); } return this._pendingTokenRequests[this.portal]; }; /** * Refreshes the current `token` and `tokenExpires` with `username` and * `password`. */ UserSession.prototype.refreshWithUsernameAndPassword = function (requestOptions) { var _this = this; var options = tslib_1.__assign({ params: { username: this.username, password: this.password, expiration: this.tokenDuration, } }, requestOptions); return generate_token_1.generateToken(this.portal + "/generateToken", options).then(function (response) { _this._token = response.token; _this._tokenExpires = new Date(response.expires); return _this; }); }; /** * Refreshes the current `token` and `tokenExpires` with `refreshToken`. */ UserSession.prototype.refreshWithRefreshToken = function (requestOptions) { var _this = this; if (this.refreshToken && this.refreshTokenExpires && this.refreshTokenExpires.getTime() < Date.now()) { return this.refreshRefreshToken(requestOptions); } var options = tslib_1.__assign({ params: { client_id: this.clientId, refresh_token: this.refreshToken, grant_type: "refresh_token", } }, requestOptions); return fetch_token_1.fetchToken(this.portal + "/oauth2/token", options).then(function (response) { _this._token = response.token; _this._tokenExpires = response.expires; return _this; }); }; /** * Exchanges an unexpired `refreshToken` for a new one, also updates `token` and * `tokenExpires`. */ UserSession.prototype.refreshRefreshToken = function (requestOptions) { var _this = this; var options = tslib_1.__assign({ params: { client_id: this.clientId, refresh_token: this.refreshToken, redirect_uri: this.redirectUri, grant_type: "exchange_refresh_token", } }, requestOptions); return fetch_token_1.fetchToken(this.portal + "/oauth2/token", options).then(function (response) { _this._token = response.token; _this._tokenExpires = response.expires; _this._refreshToken = response.refreshToken; _this._refreshTokenExpires = new Date(Date.now() + (_this.refreshTokenTTL - 1) * 60 * 1000); return _this; }); }; /** * ensures that the authorizedCrossOriginDomains are obtained from the portal and cached * so we can check them later. * * @returns this */ UserSession.prototype.fetchAuthorizedDomains = function () { var _this = this; // if this token is for a specific server or we don't have a portal // don't get the portal info because we cant get the authorizedCrossOriginDomains if (this.server || !this.portal) { return Promise.resolve(this); } return this.getPortal().then(function (portalInfo) { /** * Specific domains can be configured as secure.esri.com or https://secure.esri.com this * normalizes to https://secure.esri.com so we can use startsWith later. */ if (portalInfo.authorizedCrossOriginDomains && portalInfo.authorizedCrossOriginDomains.length) { _this.trustedDomains = portalInfo.authorizedCrossOriginDomains .filter(function (d) { return !d.startsWith("http://"); }) .map(function (d) { if (d.startsWith("https://")) { return d; } else { return "https://" + d; } }); } return _this; }); }; return UserSession; }()); exports.UserSession = UserSession; //# sourceMappingURL=UserSession.js.map