OK so I have DefaultAuthenticationChallengeHandler working fine to login to a runtime application with the following code which works great for logging in ...
AuthenticationChallengeHandler handler = new DefaultAuthenticationChallengeHandler(this);
OAuthConfiguration oAC = new OAuthConfiguration(urlLogin, Client_ID, "my-arcgis-app://auth", 129600); //90 days
AuthenticationManager.addOAuthConfiguration(oAC);
AuthenticationManager.setAuthenticationChallengeHandler(handler);
However the DefaultAuthenticationChallengeHandler works poorly for Web Maps in that the challenge occurs for every single layer in every feature service. Nobody wants to login this many times. When I use a custom login form for feature services I can suppress the multiple logins and use the first login to provide access to the rest of the layers in a feature service. I can do this via a CountDownLatch to suppress the calling thread and wait for the thread firing off the login form for a specific feature service (see code below). The issue I am having is I do like the DefaultAuthenticationChallengeHandler for login to the application because it allows me to not have to handle the username or password myself, but do not like the way it works with feature services.
Is there a way to setup a AuthenticationChallengeResponse listener against the DefaultAuthenticationChallengeHandler so that I could add a CountDownLatch to the default login?
Below is using a custom login form to access a feature service with CountDownLatch.....
AuthenticationManager.setAuthenticationChallengeHandler(this);
@Override
public AuthenticationChallengeResponse handleChallenge(AuthenticationChallenge authenticationChallenge) {
try {
if (authenticationChallenge.getType() == AuthenticationChallenge.Type.USER_CREDENTIAL_CHALLENGE) {
if (((Portal) authenticationChallenge.getRemoteResource()).getLoadStatus() == LoadStatus.LOADED) {
return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CANCEL,
authenticationChallenge);
}
int maxAttempts = 5;
if (authenticationChallenge.getFailureCount() > maxAttempts) {
// exceeded maximum amount of attempts. Act like it was a cancel
Toast.makeText(this, "Exceeded maximum amount of attempts. Please try again!", Toast.LENGTH_LONG).show();
return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CANCEL,
authenticationChallenge);
}
String fsURL = authenticationChallenge.getRemoteResource().getUri();
if (!fsURL.endsWith("/FeatureServer")) {
if (fsURL.contains("/FeatureServer")) {
fsURL = fsURL.substring(0, fsURL.indexOf("/FeatureServer") + 14);
}
}
final String fsURLPassIN = fsURL;
if (mAgencyCredentials==null) {
authenticationCounter++;
DownloadHelper.signal = new CountDownLatch(1);
runOnUiThread(new Runnable() {
@Override
public void run() {
showFSLogin("Credential is required to access " + authenticationChallenge.getRemoteResource().getUri(), fsURLPassIN);
}
});
try {
DownloadHelper.signal.await();
} catch (InterruptedException e) {
String error = "Interruption handling AuthenticationChallengeResponse: " + e.getMessage();
Toast.makeText(this, error, Toast.LENGTH_LONG).show();
}
}
// if credentials were set, return a new auth challenge response with them. otherwise, act like it was a cancel
if (mAgencyCredentials != null) {
if (authenticationCounter==0) { //this should be only run once after authentication as this challenge response will hit once for each layer.
loadMap();
}
authenticationCounter++;
return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CONTINUE_WITH_CREDENTIAL, mAgencyCredentials);
}
}
} catch (Exception e){
e.printStackTrace();
}
// no credentials were set, return a new auth challenge response with a cancel
return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CANCEL, authenticationChallenge);
}