IdentityManager.Current.GenerateCredentialAsync on ArcGIS Portal not working

1619
6
04-14-2021 09:07 PM
jgalarse
New Contributor II

Hi, we are currently trying to transition to ArcGIS Enterprise from ArcGIS Server. We have a sample map service that we published as a WFS layer which has all of the functions we need. We can access the map if we set the "Share" field of the map to "Everyone" but the map won't load if we use "Organization" or "Owner" even if we set the IdentityManager credential as the same user that created the map.

This is how we set the credential:

var cred = await IdentityManager.Current.GenerateCredentialAsync("https://<our portal site>/portal/sharing/rest/", "username", "password");

IdentityManager.Current.AddCredential(cred);

To confirm that the credential is working, I tried adding:

await Esri.ArcGISRuntime.Portal.ArcGISPortal.CreateAsync(new Uri(_portalUrl));

The resulting portal object does show the CurrentUser object as the user that we added. Also, we get errors if we use an invalid user.

We then create our layer and add it to the map using the following code:

Map dynamicMap = new Esri.ArcGISRuntime.Controls.Map
{
	Layers = new LayerCollection()
};

ArcGISDynamicMapServiceLayer layer = new ArcGISDynamicMapServiceLayer(new Uri("https://<our portal server>/server/rest/services/Hosted/PortalTest2WFS/MapServer"));
layer.Opacity = 1;

await layer.InitializeAsync();

dynamicMap.Layers.Add(layer);

MapView.Map = dynamicMap;

 

When we set the WFS map's Share to "Everyone", the map loads fine. If we set it back to "Organization" or "Everyone", it doesn't load anything. I don't get any errors as well.

 

Please help 🙂

0 Kudos
6 Replies
thomasbales
Occasional Contributor

I haven't looked too much at the code but I think you may be trying to load the layer before the credentials have been set. To test, add a  thread sleep for a few seconds after you set the cred.

0 Kudos
JenniferNery
Esri Regular Contributor

Does layer.InitializeAsync trigger a challenge if you subscribe to ChallengeHandler? 

 

IdentityManager.Current.ChallengeHandler = new ChallengeHandler(async (info) =>
{
  var cred = await 
  IdentityManager.Current.GenerateCredentialAsync(info.ServiceUri, "<username>", "<password>");
  return cred;
});

 

It may be possible that you need to AddCredential for ServiceUri that match server 

https://<portal server>/server/rest

 

0 Kudos
jgalarse
New Contributor II

Thanks Jennifer. Forgot to mention that we are using the ArcGIS .NET SDK v10.2.7. I tried adding the ChallengeHandler but it doesn't seem to get triggered. I'm curious as to what other method can I try to trigger the ChallengeHander event.

0 Kudos
JoeHershman
MVP Regular Contributor

I am not sure if this is the same issue, or if it has been fixed in a later version of Runtime (we are still on 100.6).  But I have seen a timing issue with credentials.  It is discussed a bit here:

Offline Map Sync Error: Job error 6004...

I am not sure if this is related but was happening when I tried to sync soon after connecting the credentials had not been been applied to everything in my offline map.  So I got credentials errors.  Even though I set the credentials.  

What I do to solve this is basically make a dummy hit against all layers before actually trying to do the real load of them.

Basically I load a layer, don't worry if it gives me an error and then load the layer again and the second time the token has been properly applied.

 

/// <summary>
/// This is used because of issue with the timing issues associated to
/// getting tokens in first sync
/// </summary>
/// <returns></returns>
private async Task InitialzeTokens(Map map)
{
	if ( !_firstSync ) return;
	 
	foreach (var featureLayer in map.OperationalLayers.OfType<FeatureLayer>())
	{
		var pars = new QueryParameters {WhereClause = "1=0", MaxFeatures = 1};

		if ( !(featureLayer.FeatureTable is GeodatabaseFeatureTable gdbFeatureTable) ) continue;
		if ( gdbFeatureTable.Geodatabase?.Source == null ) continue;

		var serviceFeatureTable = new ServiceFeatureTable(gdbFeatureTable.Geodatabase.Source);

		try
		{
			await serviceFeatureTable.LoadAsync();
			var _ = await serviceFeatureTable.QueryFeaturesAsync(pars);
		}
		catch (Exception)
		{
			//_log.Error(e, e.Message);
		}
	}

	_firstSync = false;
}

 

Also along the lines of what @JenniferNery describes.  Instead of doing

 

IdentityManager.Current.AddCredential(cred);

 

Use the ChallengeHandler and you can see when it requests credentials

 

//put in constructor
AuthenticationManager.Current.ChallengeHandler = new ChallengeHandler(CreateCredentialAsync);

private async Task<Credential> CreateCredentialAsync(CredentialRequestInfo info)
{
	try
	{
		_log.Info($"CHALLENGE  ********** : {info.ServiceUri} ***********");
		if ( info.ServiceUri == null ) return null;

		var credential = await AuthenticationManager.Current.GenerateCredentialAsync(info.ServiceUri, Settings.UserName, Settings.Password);

		_log.Info($"RETURN credential *********: {info.ServiceUri} ********");
		return credential;

	}
	catch (Exception e)
	{
		_log.Error(e, e.Message);

		return null;
	}
}

 

Good Luck

Thanks,
-Joe
0 Kudos
jgalarse
New Contributor II

Thanks Joe. I forgot to mention that we are still in ArcGIS SDK .NET 10.2.7. AuthenticationManager doesn't seem to be in sdk, it does have the IdentityManager. I tried what @JenniferNery  suggested but the InitializeAsync doesn't seem to trigger the ChallengeHander event.

I'm curious what triggers this event?

0 Kudos
JoeHershman
MVP Regular Contributor

ChallangeHandler gets triggered when credentials are required.  If you are already calling 

IdentityManager.Current.AddCredential(cred);

in code than credentials are not required and so the ChallengeHandler would not be called.  ChallengeHandler is a nice pattern to centralize all requests for credentials in a single place, and there is no need to specifically add credentials prior to accessing a resource

Thanks,
-Joe
0 Kudos