Document how consuming apps can login when an unauthenticated request fails with an auth error
See original GitHub issueThe scenario that we want to support is to allow an unauthenticated user to make requests via any of the arcgis-rest-js fns (getItem()
, getItemDetails()
, etc) and only a the point when they request a secured resource, will they be prompted to log in.
This request is currently coming from @qlqllu but will likely be something that others who are familiar w/ the default behavior of IdentityManager will want as well.
Obviously there are challenges to accommodating this kind of behavior due to the stateless nature of our API and the fact that it needs to run in both the browser and node and therefore has to support different authorization schemes. However, I’ve done some experiments, and I have what I think is a reasonable solution for this.
I propose that we add a property like autoAuthentication
to IRequestOptions
that is a function that returns a promise that resolves with an instance of an IAuthenticationManager
. So we’d add this to IRequestOptions
:
/**
* Use this function to login and retry when an unauthenticated request fails with an authorization error
*/
autoAuthentication?: (originalRequestOptions: IRequestOptions) => Promise<IAuthenticationManager>
Then, if an unauthenticated request fails with an ArcGISAuthError, and an autoAuthentication
function was passed in, call that function and retry the request with the auth that was returned. It’s up to the consuming application to decide how to implement autoAuthentication()
. Browser apps can provide their own sign in dialog using their framework of choice, or kick off the OAuth flow. Node apps can create a new UserSession
or ApplicationSession
using credentials they get from the environment or from prompting the user. Note that we pass the original request options to autoAuthentication
in case the consuming application needs the URL or other info to decide how to get the credentials.
Here’s an example of what this might look like in a browser app:
// send this w/ all requests, it will be undefined until the user requests a secure resource and is prompted to sign in
let userSession;
// use this to auto sign in user when the first unauthenticated request fails
signIn = (orginalRequestOptions) => {
const url = orginalRequestOptions.url;
if (url.indexOf('sharing/rest') > -1) {
// this is going to the portal API (items, groups, etc), kick off OAuth sign in
const oauth2Options = {
// NOTE: you could base portal URL and/or clientId of the orignialRequestOptions
portal: "https://www.arcgis.com/sharing/rest",
clientId: 'getyourown',
redirectUri: window.location.origin + location.pathname,
popup: true
};
return UserSession.beginOAuth2(oauth2Options)
.then(newUserSession => {
// hold on to user session and use that for future requests
userSession = newUserSession;
return newUserSession;
});
} else {
// this is going to a server (feature service, etc)
// TODO: we could either throw an error, or have a system to store multiple credentials and
// show a dialog to gather user credentials and send a request to generate token
}
}
// request the data for an unsecured application
getItemData('3aef...', {
// neither of these will be used since the item is shared w/ everyone
authentication: userSession,
autoAuthentication: !!userSession ? undefined : signIn
}).then(data => {
// now request a secured webmap
getItem('4beg...', {
// userSession is still undefined
authentication: userSession,
// signIn will be called when this request fails
autoAuthentication: !!userSession ? undefined : signIn
}).then(webMapItem => {
// now get the webmap's data
getItemData('4beg...', {
// userSession is now defined, so this will provide the token for this request
authentication: userSession,
// signIn will not be passed in since it is no longer needed
autoAuthentication: !!userSession ? undefined : signIn
}).then(webMapData => {
// do something with the webmap item/data
});
});
});
Issue Analytics
- State:
- Created 5 years ago
- Comments:10 (10 by maintainers)
Top GitHub Comments
@patrickarlt++ for thinking ahead. I agree that
IdentityManager
’s uber generic solution is overkill for most apps outside of the context of a webmap that may be pulling layers from who knows where.I’ve updated my auto-login demo to use arcgis-rest-js’s built-in retry() method as @patrickarlt suggested above, and it works well.
So, the only thing left to do in this issue is add better docs for the existing capabilities. I’m not sure we could fit a meaningful example inline w/ the existing retry() docs. This seems like it should be a guide, perhaps a sub-topic of Authentication in Browser-based Apps
@tomwayson
UserSession
already handles a lot ofIdentityManager
like trust scenarios like what you are talking about in https://github.com/Esri/arcgis-rest-js/issues/224#issuecomment-398153456.Each
UserSession
keeps its own cache oftrustedServers
https://github.com/Esri/arcgis-rest-js/blob/master/packages/arcgis-rest-auth/src/UserSession.ts#L241-L246 which it knows are federated with the originalportal
.So using the above
UserSession
would work for any server federated withhttps://gis.somewhere.org/sharing/rest
. TheUserSession.getTokenForServer()
method is is used to fetch tokens for federated servers we do not trust yet and establish trust by having the portal generate a token for the server.UserSession
also implicitly trusts otherarcgis.com
domains if you havehttps://www.arcgis.com/sharing/rest
set as your portal to make less requests.If we cannot establish trust like in the following case we will get an
ArcGISAuthError
whereArcGISAuthError.code === 'NOT_FEDERATED'
then the developer can useArcGISAuthError.url
andArcGISAuthError.retry
to get the user signed into the correct portal.I did of this with the interest of NOT doing any sort of
IdentityManager
instead I focused on building the tools so developers could manage however many instances ofUserSession
they wanted. I have issues with howIdentityManager
gets authentication details from you when it needs to get data from another portal or unfederated service because it doesn’t use OAuth for those authentications it just asks for your username and password. There is no way to trust it which I think makes this a huge security hole but there really isn’t any way to handle it.I always have the impression that the design and implementation of
IdentityManager
is great for ArcGIS Online since it seamlessly handles an infinite amount of federation, service and portals but is total overkill for most developers who are only going to be working with 1-2 portals.