Connect to an IWA secured portal and search for maps.

Use case
Your organization might use Integrated Windows Authentication (IWA) to secure ArcGIS Enterprise. This can be useful because the same credentials used to log into your work computer and network can be used to authenticate with ArcGIS. IWA is built into Microsoft Internet Information Server (IIS) and works well for intranet applications, but isn’t always practical for internet apps.
How to use the sample
Enter the URL to your IWA-secured portal. Click the ‘Search IWA Secured Portal’ button to search for web maps stored on the portal. You will be prompted for a user name, password, and domain. If you authenticate successfully, portal item results will display in the list. Select a web map item to display it in the map view.
How it works
- The
AuthenticationManagerobject is configured with a challenge handler that will prompt for a Windows login (username, password, and domain) if a secure resource is encountered. - When a search for portal items is performed against an IWA-secured portal, the challenge handler creates an
UserCredentialobject from the information entered by the user. - If the user authenticates, the search returns a list of web map
PortalItems and the user can select one to display as anArcGISMap.
Relevant API
- AuthenticationChallenge
- AuthenticationChallengeHandler
- AuthenticationChallengeResponse
- AuthenticationManager
- Portal
- UserCredential
About the data
This sample searches for web map portal items on a secure portal. To successfully run the sample, you need:
- Access to a portal secured with Integrated Windows Authentication that contains one or more web map items.
- A login that grants you access to the portal.
Additional information
More information about IWA and its use with ArcGIS can be found at the following links:
Tags
authentication, security, Windows
Sample Code
/* * Copyright 2022 Esri. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */
module com.esri.samples.integrated_windows_authentication { // require ArcGIS Maps SDK for Java module requires com.esri.arcgisruntime;
// handle SLF4J http://www.slf4j.org/codes.html#StaticLoggerBinder requires org.slf4j.nop;
// require JavaFX modules that the application uses requires javafx.graphics; requires javafx.controls; requires javafx.fxml;
// make all @FXML annotated objects reflectively accessible to the javafx.fxml module opens com.esri.samples.integrated_windows_authentication to javafx.fxml;
exports com.esri.samples.integrated_windows_authentication;}/* * Copyright 2019 Esri. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */
package com.esri.samples.integrated_windows_authentication;
import javafx.fxml.FXML;import javafx.fxml.FXMLLoader;import javafx.scene.control.Alert;import javafx.scene.control.ButtonType;import javafx.scene.control.Dialog;import javafx.scene.control.PasswordField;import javafx.scene.control.TextField;
import com.esri.arcgisruntime.security.UserCredential;
class AuthenticationDialog extends Dialog<UserCredential> {
@FXML private TextField userDomain; @FXML private PasswordField password; @FXML private ButtonType cancelButton; @FXML private ButtonType continueButton;
AuthenticationDialog() {
FXMLLoader loader = new FXMLLoader(getClass().getResource("/integrated_windows_authentication/iwa_auth_dialog.fxml")); loader.setRoot(this); loader.setController(this);
setTitle("Authenticate");
try { loader.load(); } catch (Exception e) { e.printStackTrace(); }
setResultConverter(dialogButton -> { if (dialogButton == continueButton) { if (!userDomain.getText().equals("") && !password.getText().equals("")) { try { return new UserCredential(userDomain.getText(), password.getText()); } catch (Exception e) { new Alert(Alert.AlertType.ERROR, e.getMessage()).show(); } } else { new Alert(Alert.AlertType.ERROR, "Missing credentials").show(); } } return null; }); }
}/* * Copyright 2019 Esri. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */
package com.esri.samples.integrated_windows_authentication;
import java.util.concurrent.CountDownLatch;
import javafx.application.Platform;import javafx.scene.control.Alert;
import com.esri.arcgisruntime.loadable.LoadStatus;import com.esri.arcgisruntime.portal.Portal;import com.esri.arcgisruntime.security.AuthenticationChallenge;import com.esri.arcgisruntime.security.AuthenticationChallengeHandler;import com.esri.arcgisruntime.security.AuthenticationChallengeResponse;import com.esri.arcgisruntime.security.SelfSignedResponse;import com.esri.arcgisruntime.security.UserCredential;
/** * Handler to be used when accessing a secured resource. */final class IWAChallengeHandler implements AuthenticationChallengeHandler {
private UserCredential userCredential;
/** * Handles challenge before accessing a secured resource. * * @param authenticationChallenge the authentication challenge to handle * @return the AuthenticationChallengeResponse indicating which action to take */ @Override public AuthenticationChallengeResponse handleChallenge(AuthenticationChallenge authenticationChallenge) {
// if the authentication challenge is self signed, return the appropriate response if (authenticationChallenge.getType() == AuthenticationChallenge.Type.SELF_SIGNED_CHALLENGE) { return new AuthenticationChallengeResponse( AuthenticationChallengeResponse.Action.CONTINUE_WITH_SELF_SIGNED_RESPONSE, new SelfSignedResponse(true, true));
} else if (authenticationChallenge.getType() == AuthenticationChallenge.Type.USER_CREDENTIAL_CHALLENGE && authenticationChallenge.getRemoteResource() instanceof Portal) {
// If challenge has been requested by a Portal and the Portal has been loaded, cancel the challenge // This is required as some layers have private portal items associated with them and we don't // want to auth against them 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 new Alert(Alert.AlertType.ERROR, "Exceeded maximum amount of attempts. Please try again!").show(); return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CANCEL, authenticationChallenge); }
// create a countdown latch with a count of one to synchronize the authentication dialog CountDownLatch authenticationCountDownLatch = new CountDownLatch(1); // show the authentication dialog and capture the user credentials Platform.runLater(() -> { AuthenticationDialog authenticationDialog = new AuthenticationDialog(); authenticationDialog.show(); authenticationDialog.setOnCloseRequest(r -> { userCredential = (UserCredential) authenticationDialog.getResult(); authenticationCountDownLatch.countDown(); }); });
try { authenticationCountDownLatch.await(); } catch (InterruptedException e) { new Alert(Alert.AlertType.ERROR, "Interruption handling AuthenticationChallengeResponse: " + e.getMessage()).show(); }
// if credentials were set, return a new auth challenge response with them. otherwise, act like it was a cancel action if (userCredential != null) { return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CONTINUE_WITH_CREDENTIAL, userCredential); } }
// no credentials were set, return a new auth challenge response with with a cancel action return new AuthenticationChallengeResponse(AuthenticationChallengeResponse.Action.CANCEL, authenticationChallenge); }}/* * Copyright 2019 Esri. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */
package com.esri.samples.integrated_windows_authentication;
import java.util.List;import java.util.concurrent.ExecutionException;
import javafx.fxml.FXML;import javafx.scene.control.Alert;import javafx.scene.control.ListView;import javafx.scene.control.ProgressIndicator;import javafx.scene.control.TextField;import javafx.scene.text.Text;
import com.esri.arcgisruntime.concurrent.ListenableFuture;import com.esri.arcgisruntime.loadable.LoadStatus;import com.esri.arcgisruntime.mapping.ArcGISMap;import com.esri.arcgisruntime.mapping.view.DrawStatus;import com.esri.arcgisruntime.mapping.view.MapView;import com.esri.arcgisruntime.portal.Portal;import com.esri.arcgisruntime.portal.PortalItem;import com.esri.arcgisruntime.portal.PortalQueryParameters;import com.esri.arcgisruntime.portal.PortalQueryResultSet;import com.esri.arcgisruntime.security.AuthenticationManager;
public class IntegratedWindowsAuthenticationController {
@FXML private MapView mapView; @FXML private ListView<PortalItem> resultsListView; @FXML private TextField portalUrlTextField; @FXML private ProgressIndicator progressIndicator; @FXML private Text loadWebMapTextView;
private Portal iwaSecuredPortal; // keep loadable in scope to avoid garbage collection
public void initialize() { try {
// set authentication challenge handler AuthenticationManager.setAuthenticationChallengeHandler(new IWAChallengeHandler());
// add a listener to the map results list view that loads the map on selection resultsListView.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> { if (resultsListView.getSelectionModel().getSelectedItem() != null) { // show progress indicator while map is drawing progressIndicator.setVisible(true);
// get the portal item ID from the selected list view item PortalItem portalItem = resultsListView.getSelectionModel().getSelectedItem();
if (portalItem != null) { // create a Map using the web map (portal item) ArcGISMap webMap = new ArcGISMap(portalItem); // set the map to the map view mapView.setMap(webMap); loadWebMapTextView.setText("Loaded web map from item " + portalItem.getItemId());
}
} });
// make the list view show a preview of the portal items' map area resultsListView.setCellFactory(c -> new PortalItemInfoListCell());
// hide the progress indicator when the map is finished drawing mapView.drawStatusProperty().addListener((observable, oldValue, newValue) -> { if (newValue == DrawStatus.COMPLETED) { progressIndicator.setVisible(false); } });
} catch (Exception e) { // on any error, display the stack trace. e.printStackTrace(); } }
/** * Handles searching the provided IWA secured portal. */ @FXML private void handleSearchPortalPress() { // check for a url in the URL field if (portalUrlTextField.getText().isEmpty()) { new Alert(Alert.AlertType.ERROR, "Portal URL is empty. Please enter a portal URL.").show();
} else { // prefix the entered URL with 'https://' if it is not already String portalUrl = portalUrlTextField.getText(); if (!portalUrl.startsWith("https://")){ portalUrl = "https://" + portalUrl; }
// keep hold of the portal we are searching and set a variable indicating that this is a secure portal, to allow retrieving portal items later iwaSecuredPortal = new Portal(portalUrl, true);
// clear any existing items in the list view resultsListView.getItems().clear();
// clear the information about the previously loaded map loadWebMapTextView.setText("");
// show progress indicator progressIndicator.setVisible(true);
// load the portal items iwaSecuredPortal.loadAsync(); iwaSecuredPortal.addDoneLoadingListener(() -> { if (iwaSecuredPortal.getLoadStatus() == LoadStatus.LOADED) {
// create portal query parameters searching for web maps PortalQueryParameters portalQueryParameters = new PortalQueryParameters( "type:(\"web map\" NOT \"web mapping application\")");
// search the portal for web maps ListenableFuture<PortalQueryResultSet<PortalItem>> portalItemResultFuture = iwaSecuredPortal.findItemsAsync( portalQueryParameters); portalItemResultFuture.addDoneListener(() -> { try { // get the result PortalQueryResultSet<PortalItem> portalItemSet = portalItemResultFuture.get(); List<PortalItem> portalItems = portalItemSet.getResults(); // add the IDs and titles of the portal items to the list view resultsListView.getItems().addAll(portalItems);
} catch (ExecutionException | InterruptedException e) { new Alert(Alert.AlertType.ERROR, "Error getting portal item set from portal.").show(); } // hide the progress indicator progressIndicator.setVisible(false); });
} else { // hide the progress indicator progressIndicator.setVisible(false);
// report error new Alert(Alert.AlertType.ERROR, "Portal sign in failed").show();
} }); } }
/** * Stops and releases all resources used in the application. */ void terminate() {
if (mapView != null) { mapView.dispose(); } }
}/* * Copyright 2019 Esri. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */
package com.esri.samples.integrated_windows_authentication;
import javafx.application.Application;import javafx.fxml.FXMLLoader;import javafx.scene.Parent;import javafx.scene.Scene;import javafx.stage.Stage;
public class IntegratedWindowsAuthenticationSample extends Application {
private static IntegratedWindowsAuthenticationController controller;
@Override public void start(Stage stage) throws Exception { FXMLLoader loader = new FXMLLoader(getClass().getResource("/integrated_windows_authentication/main.fxml")); Parent root = loader.load(); controller = loader.getController(); Scene scene = new Scene(root);
// set title, size, and add scene to stage stage.setTitle("Integrated Windows Authentication Sample"); stage.setWidth(800); stage.setHeight(700); stage.setScene(scene); stage.show(); }
/** * Stops and releases all resources used in application. */ @Override public void stop() { controller.terminate(); }
/** * Opens and runs application. * * @param args arguments passed to this application */ public static void main(String[] args) {
Application.launch(args); }}package com.esri.samples.integrated_windows_authentication;
import javafx.scene.control.ListCell;
import com.esri.arcgisruntime.portal.PortalItem;
/** * Shows the title of the portal items in the selection list view. */class PortalItemInfoListCell extends ListCell<PortalItem> {
@Override protected void updateItem(PortalItem portalItem, boolean empty) { super.updateItem(portalItem, empty); if (portalItem != null) { // set the list cell's text to the map's index setText(portalItem.getTitle()); } else { setText(null); } }}