Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(arcgis-rest-request): add support for server credentials #965

Merged
merged 8 commits into from
Mar 22, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 5 additions & 17 deletions demos/jsapi-integration/authenticate.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,12 @@
</head>
<body>
<script src="@esri/arcgis-rest-request/dist/bundled/request.umd.js"></script>
<script src="./config.js"></script>
<script>
/* in a production app, clientId could be hardcoded. here we're using a regex to extract it from the state parameter in the OAuth2 server response to make the demo more interchangeable.

this relies on the fact that the ClientId is associated with the state parameter internally when another value isn't supplied manually.
*/
const match = window.location.href.match(
/&state=([^&]+)/
);
const clientId = match[1];
let session;
function processAuthentication() {
window.location.href = './';
session = arcgisRest.UserSession.completeOAuth2({
clientId: clientId,
});
localStorage.setItem('__ARCGIS_REST_USER_SESSION__', session.serialize());
}
processAuthentication();
arcgisRest.ArcGISIdentityManager.completeOAuth2({
clientId: config.clientId,
redirectUrl: config.redirectUri
})
</script>
</body>
</html>
5 changes: 4 additions & 1 deletion demos/jsapi-integration/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,7 @@ You can generate your own clientid by creating an application on the ArcGIS for

once you have a clientid of your own, copy/paste it here and rename this file 'config.js'
*/
let clientId = "S19JZF92TRf2HZit";
const config = {
clientId: "3CiiHWyTNMIRNyF1",
redirectUri: "http://localhost:8080/authenticate.html"
};
54 changes: 31 additions & 23 deletions demos/jsapi-integration/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,25 @@
<body>
<div id="viewDiv"></div>
<script>
// First, log in
const redirect_uri = window.location.origin + window.location.pathname + "authenticate.html";

arcgisRest.UserSession.beginOAuth2({
clientId: clientId,
redirectUri: redirect_uri,
// create a variable to store our session.
let session;
arcgisRest.ArcGISIdentityManager.beginOAuth2({
clientId: config.clientId,
redirectUri: config.redirectUri,
popup: true
}).then(session => {
// Next, fetch a private item's data (not just the metadata)
//
// Change this to an itemId of a private webmap that you have access too.
const itemId = "75f08b51fa714d9c863aaf002dfba0c6";

return arcgisRest.getItemData(itemId, { authentication: session })
}).then(response => {
}).then(s => {
session = s;
// fetch the users items (up to 100)
return arcgisRest.getUserContent({ authentication: session, num: 100 })
}).then(userContent =>{
// find a web map in the users items
const webMapItem = userContent.items.find(t => t.type === "Web Map");
return webMapItem;
} ).then(webMapItem => {
if(!webMapItem) {
alert("No web map found in first 100 items");
return
}

// once we have the private item, we can create a map
// NOTE: this is where you would lazy-load the ArcGIS API for JavaScript
Expand All @@ -62,14 +66,12 @@
// JavaScript Identity Manager
esriId.registerToken(session.toCredential());

// display the title of its basemap
console.debug(response);
console.debug('baseMap title:', response.baseMap.title);
console.debug(webMapItem);
patrickarlt marked this conversation as resolved.
Show resolved Hide resolved

// create a map with the item
var webmap = new WebMap({
portalItem: {
id: itemId
id: webMapItem.id
}
});

Expand All @@ -79,11 +81,17 @@
container: "viewDiv"
});

// you can also pass credentials in the other direction if you have credentials in teh JS API IdentityManager class
esriId.getCredential("https://arcgis.com/sharing/rest/")
.then(cred => {
const session2 = arcgisRest.UserSession.fromCredential(cred);
});
// Once the web map loads the JS API should be "aware" of the ArcGIS.com portal.
// Using the findCredential and findServerInfo methods we can create an ArcGISIdentityManager
// for use in REST JS from the JS API
webmap.watch("loaded", function (loaded) {
if(loaded) {
const credential = esriId.findCredential("https://arcgis.com/sharing/rest/");
const serverInfo = esriId.findServerInfo("https://arcgis.com/sharing/rest/");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two things for this demo:

  1. Should we explain why loading specifically a web map is required vs any other item? (I assume because it ensures portal and server credentials)
  2. The README is fairly out of date at this point and the code snippet doesn't match this new fromCredential logic.

const session2 = arcgisRest.ArcGISIdentityManager.fromCredential(credential, serverInfo);
console.log(session2);
patrickarlt marked this conversation as resolved.
Show resolved Hide resolved
}
});
});
});
</script>
Expand Down
68 changes: 52 additions & 16 deletions packages/arcgis-rest-request/src/ArcGISIdentityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ export interface IFromTokenOptions {
token: string;
tokenExpires?: Date;
portal?: string;
server?: string;
username?: string;
}

/**
Expand Down Expand Up @@ -60,6 +62,17 @@ export interface ICredential {
userId: string;
}

/**
* Represents the [`ServerInfo`](https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-ServerInfo.html) class
* in the ArcGIS API for JavaScript.
*/
export interface IServerInfo {
server: string;
hasPortal: boolean;
hasServer: boolean;
owningSystemUrl: string | null;
}

/**
* Options for static OAuth 2.0 helper methods on `ArcGISIdentityManager`.
*/
Expand Down Expand Up @@ -801,27 +814,48 @@ export class ArcGISIdentityManager implements IAuthenticationManager {
}

/**
* Translates authentication from the format used in the [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/).
* Translates authentication from the format used in the [`IdentityManager` class in the ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-Credential.html).
*
* You will need to call both [`IdentityManger.findCredential`](https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-IdentityManager.html#findCredential) and [`IdentityManger.findServerInfo`](https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-IdentityManager.html#findServerInfo) to obtain both parameters for this method.
*
* This method can be used with {@linkcode ArcGISIdentityManager.toCredential} to interop with the ArcGIS API for JavaScript.
*
* ```js
* ArcGISIdentityManager.fromCredential({
* userId: "jsmith",
* token: "secret"
* require(["esri/id"], (esriId) => {
* const credential = esriId.findCredential("https://www.arcgis.com/sharing/rest");
* const serverInfo = esriId.findServerInfo("https://www.arcgis.com/sharing/rest");
*
* const manager = ArcGISIdentityManager.fromCredential(credential, serverInfo);
* });
* ```
*
* @returns ArcGISIdentityManager
*/
public static fromCredential(credential: ICredential) {
public static fromCredential(
credential: ICredential,
serverInfo: IServerInfo
) {
// 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
const ssl = typeof credential.ssl !== "undefined" ? credential.ssl : true;
const expires = credential.expires || Date.now() + 7200000; /* 2 hours */

if (serverInfo.hasServer) {
return new ArcGISIdentityManager({
server: credential.server,
ssl,
token: credential.token,
username: credential.userId,
tokenExpires: new Date(expires)
});
}
console.log(credential.server.includes("sharing/rest"));
patrickarlt marked this conversation as resolved.
Show resolved Hide resolved
return new ArcGISIdentityManager({
portal: credential.server.includes("sharing/rest")
? credential.server
: credential.server + `/sharing/rest`,
portal: cleanUrl(
credential.server.includes("sharing/rest")
? credential.server
: credential.server + `/sharing/rest`
),
ssl,
token: credential.token,
username: credential.userId,
Expand All @@ -835,7 +869,7 @@ export class ArcGISIdentityManager implements IAuthenticationManager {
*/
private static parentMessageHandler(event: any): ArcGISIdentityManager {
if (event.data.type === "arcgis:auth:credential") {
return ArcGISIdentityManager.fromCredential(event.data.credential);
return new ArcGISIdentityManager(event.data.credential);
}
if (event.data.type === "arcgis:auth:error") {
const err = new Error(event.data.error.message);
Expand Down Expand Up @@ -1010,18 +1044,23 @@ export class ArcGISIdentityManager implements IAuthenticationManager {
}

/**
* Returns authentication in a format useable in the [ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/).
* Returns authentication in a format useable in the [`IdentityManager.registerToken()` method in the ArcGIS API for JavaScript](https://developers.arcgis.com/javascript/latest/api-reference/esri-identity-IdentityManager.html#registerToken).
*
* This method can be used with {@linkcode ArcGISIdentityManager.fromCredential} to interop with the ArcGIS API for JavaScript.
*
* ```js
* esriId.registerToken(manager.toCredential());
* require(["esri/id"], (esriId) => {
* esriId.registerToken(manager.toCredential());
* })

* ```
*
* @returns ICredential
*/
public toCredential(): ICredential {
return {
expires: this.tokenExpires.getTime(),
server: this.portal,
server: this.server || this.portal,
ssl: this.ssl,
token: this.token,
userId: this.username
Expand Down Expand Up @@ -1301,10 +1340,7 @@ export class ArcGISIdentityManager implements IAuthenticationManager {
if (isValidOrigin && isValidType) {
let msg = {};
if (isTokenValid) {
const credential = this.toCredential();
// 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-request/post-message-auth-spec.md#arcgisauthcredential
credential.server = credential.server.replace("/sharing/rest", "");
const credential = this.toJSON();
msg = {
type: "arcgis:auth:credential",
credential
Expand Down
Loading