@esri/arcgis-rest-auth
Version:
Authentication helpers for @esri/arcgis-rest-js.
911 lines • 39.7 kB
JavaScript
"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<UserSession>. 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