Hi all,
I've recently updated to ArcGIS Unity SDK v2 from v1.6 . And as mentioned in their documentation, the previously working user authentication mechanism I'd implemented is now broken with v2.0. What I'd do usually is:
Previously I could generate a new ArcGISOAuthAuthorizationCredential object with these tokens. But with v.2 I'm running into an error that seems to be dll related since the RT_GEOAuthAuthorizationCredential_create doesnt seem to exist.
Could someone please help me understand how to proceed with ArcGIS Unity SDK v2.0 after getting these tokens? The sample code, scenes, and documentation provided for v2.0 are, honestly, as confusing as how they have always been previously.
Hi Geoffrey,
You are correct, RT_GEOAuthAuthorizationCredential_create doesn't exist anymore. The most straight-forward method for user authentication is to create OAuth User Configuration(s) for the content you are accessing either through the UI or the API (in case of API you can add the configuration to ArcGISAuthenticationManager.OAuthUserConfigurations, as done in the OAuthScene script)
There are two separate scripts that take care of the steps you mentioned above: ArcGISEditorOAuthUserLoginPromptHandler (used for edit-time login) and SampleOAuthUserLoginPromptHandler (used for standalone apps and optionally play-mode in editor).
To enable the latter, you need to add a SampleAuthenticationHandlerInitializer component to the arcgis map.
These scripts will open the browser for login and process the authorization code to create the needed ArcGISCredential object(s). These credentials are stored in ArcGISCredentialStore which are reused for authorizing any other matching content and are automatically refreshed (if applicable).
If you want to implement your own process for handling the login, the easiest way is to modify the HandleOAuthUserLoginPrompt method in SampleOAuthUserLoginPromptHandler to return the response uri string you retrieved using your own method for getting the authorization code (should be something like "<redirect uri>?code=<authorization code>"). That authorization code will be used to add an ArcGISCredential to the ArcGISCredentialStore. If you need to read the generated access and/or refresh token, you can get them through the credential store (find the right credential in store, create an ArcGISOAuthUserCredential using its handle, and perform a GetTokenInfoAsync).
If you do not wish to create oauth user configurations beforehand, there is a way to go around it, but I dont recommend it since you would have to implement additional mechanisms for managing the pending authentication challenges.
One final note: SampleOAuthUserLoginPromptHandler is a sample code. We recommend adjusting it to the security needs of your project.
Hope this helps.
Hi @AShahbaz the sample code seems incomplete from my POV. I've modified the SampleOAuthUserLoginPromptHandler method to return a uri of the form <redirect uri>?code=<authorization code>.
But it doesn't automatically seem to add an ArcGISCredential to the ArcGISCredentialStore like you were hinting at. Are there any other steps that I'm missing here? Do I need to create a challenge handler somewhere or initialize ArcGISRuntimeEnvironment.AuthenticationManager.CfredentialStore?
Are there any other steps that I'm missing here? Do I need to create a challenge handler somewhere or initialize ArcGISRuntimeEnvironment.AuthenticationManager.CfredentialStore?
Is there any error or warning in the console? Is the HandleOAuthUserLoginPrompt ever executed? It wont reach that point if it doesnt find a matching oauth user configuration. You shouldn't need to initialize anything beside attaching SampleAuthenticationHandlerInitializer somewhere (and possibly modifying it to point to your custom handler if you have created a new one). Can you authenticate with the original SampleOAuthUserLoginPromptHandler or the editor version?
In my app, I just get something like "A valid API Key or OAuth User Configuration is required for www.arcgis.com".
On the OAuth sample scene, I've tried using attaching the SampleAuthenticationHandlerInitializer component to an ArcGISMap and modified it to call oauthUserLoginPromptHandler.HandleOAuthUserLoginPrompt(authorize_endpoint, "http://localhost:12345/") on Start(). When I hit play on Unity Editor the Application.OpenUrl(authorizeURL) doesn't seem to do anything. The editor just gets stuck on authorizationTask.Wait().
In any case, my understanding from your suggestions has been that HandleOAuthUserLoginPrompt(authorize_endpoint, "http://localhost:12345/") would do everything needed to create and store a credential in the credentialstore. But I dont see how thats possible because its just returning a string. I'm assuming another function needs to be called to process that string somehow. ArcGISAuthenticationManager has a function OnOAuthUserLoginIssued which seems to process the url of the form "<redirect uri>?code=<authorization code>" by using a ArcGISOAuthUserLoginPrompt.Respond function. So, I'm very confused as to how an HandleOAuthUserLoginPrompt function that just returns a string and doesnt invoke any events like OAuthUserLogin is supposed to be the complete solution. Please let me know if my reasoning makes sense.
private void OnOAuthUserLoginIssued(ArcGISOAuthUserLoginPrompt prompt)
{
var response = prompt.RedirectURL + "?error=No handler provided for OAuthUserLogin.";
if (OAuthUserLoginPromptHandler != null)
{
response = OAuthUserLoginPromptHandler.HandleOAuthUserLoginPrompt(prompt.AuthorizeURL, prompt.RedirectURL);
}
prompt.Respond(response);
}
`OnOAuthUserLoginIssued` is a callback function that is called when authentication is needed for a layer. Its argument, prompt, contains the AuthorizationURL and RedirectURL. The former is the url that is used for logging in (in your case probably something like "https://www.arcgis.com/sharing/rest/oauth2/authorize?...").
From there, `OAuthUserLoginPromptHandler.HandleOAuthUserLoginPrompt(prompt.AuthorizeURL, prompt.RedirectURL)` will be called, which returns a string that should contain "?code=<authorization code>" (or "?error=..." in case of an error). That string is used to respond to the prompt. The authorization code is then extracted from the response and used for generating the access token. So, you are right, calling `HandleOAuthUserLoginPrompt` on its own has no effect, it has to be called through the event that is triggered automatically when needed. To be clear, this is just a short overview of what is happening, you don't need to change anything here.
What `OAuthUserLoginPromptHandler.HandleOAuthUserLoginPrompt` does gets a bit complicated because we differentiate between edit mode, in-editor play mode, desktop, and mobile. `OAuthUserLoginPromptHandler` is of an abstract type. Its concrete implementation is assigned based on the current mode:
I've tried using attaching the SampleAuthenticationHandlerInitializer component to an ArcGISMap and modified it to call oauthUserLoginPromptHandler.HandleOAuthUserLoginPrompt(authorize_endpoint, "http://localhost:12345/") on Start().
All `SampleAuthenticationHandlerInitializer ` does is to define what concrete handler should be used for the in-editor play mode and standalone builds. You shouldn't call the handler from there. The handler is by default SampleOAuthUserLoginPromptHandler, but you can assign a different one if you have implemented a separate script.
When I hit play on Unity Editor the Application.OpenUrl(authorizeURL) doesn't seem to do anything. The editor just gets stuck on authorizationTask.Wait().
Could your system have some security restrictions for opening a browser from other apps? There is also a chance that this line is never reached if the authentication configuration is missing or invalid (see below). For debugging purpose, you could log the authorizeURL to console right before OpenUrl() and manually open it in the browser and see what happens after log in.
In my app, I just get something like "A valid API Key or OAuth User Configuration is required for www.arcgis.com".
If you see this warning, the event that I mentioned before will not be triggered, so none of the things that I talked about will happen. This warning usually means that either the OAuth User Configuration that you created doesn't have the correct portal address or one of its required fields (client id or redirect url) are empty. Note that the portal address should include the full url (e.g., https://www.arcgis.com). There is another possibility that the configuration that was added through the API was overwritten by the map component. Generally, this shouldn't happen when using the OAuthSene script, but maybe there is a race condition on the specific platform. For test purposes, you could add the configuration to the map component through the UI rather than the API.
Here are the steps I would follow for debugging the issue:
Hi @AShahbaz , I followed the steps you suggested. I manually created an authentication configuration on a map component. I also realized I was using the wrong url for service url. Instead of the tile server url (e.g. https://tiles.arcgis.com/tiles/ ... /rest/services/SceneXYZ/SceneServer), I had to give the url of the item in the address bar (https://xyz.maps.arcgis.com/home/item.html?id=abcd12345). When I do this for the Oauth sample scene, I'm finally able to open a browser window to log into the arcgis portal and load a private layer.
But, what should be the procedure for authentication when I'm adding layers during runtime? I don't see SampleOAuthUserLoginPromptHandler.HandleOAuthUserLoginPrompt triggered when I create a layer from a script like in the SampleAPIMapCreator script. For example, when I do the following:
ArcGISLayer newLayer = new ArcGISLayer(source, arcGISLayerType, APIKey);
layerId = exampleArcGISMap.Layers.Add(newLayer);
I can change the layer authentication type in Editor to "User Authentication". But I don't see a way to do this when creating a layer via script. I'm guessing thats what is needed to trigger the authentication process in SampleOAuthUserLoginPromptHandler?
Nevermind. I was mistaken. User login does seem to get triggered when I create the private layer through script in the SampleAPIMapCreator . Maybe there's an implementation issue in my custom script. Thanks for your help so far!
When adding a layer that needs authentication through API, it's best to use an empty string as the api key (this has the same effect as assigning "User Authentication" to a layer in the editor). There are some circumstances that the oauth workflow will still be triggered if the provided api key doesn't have enough authorization, but an empty string is the best bet, if you intend to load the layer with oauth.
Additionally, there are two timelines that are important here: