How to configure IOS App to request feature services from IWA Federated server?

878
4
10-01-2019 01:12 PM
CoryDavis
New Contributor III

The organization I'm contracting for recently updated their architecture to ArcGIS Enterprise 10.7.1 and now uses IWA to federate their portal.

After updating the REST endpoints, the app is no longer able to consume the feature services on the new server. I made sure the endpoints are correct, so I believe the step I'm missing is authenticating with the identity store, but I'm not sure if that's true.

Can someone suggest a solution or point me to an article which describes the process of authenticating external devices with an IWA federated portal?

0 Kudos
4 Replies
Nicholas-Furness
Esri Regular Contributor

It should just work. A couple of things to consider.

  1. Are you able to access the services from the browser?
  2. Do you have any custom AGSAuthenticationManagerDelegate challenge handlers set up?
0 Kudos
CoryDavis
New Contributor III

1. I cannot access the services from a browser if the workstation is outside their network. That sounds like it is probably the issue. What should I tell the IT staff that would highlight this? Is there a setting which only allows services to be consumed internally?

2. We do not use a custom challenge with AGSAuthenticationManagerDelegate.

CoryDavis
New Contributor III

Their IT team has restored access to the feature server endpoint and now there is new behavior.

This is the method which initializes the feature services when the map is loaded:

func addOperationalLayers() {
        var loadedCount = 0
        DispatchQueue.main.async {
            SVProgressHUD.showProgress(0, status: "Loading Layer \(loadedCount)/27")
        }
        for i in 1 ... 27 {
            let url = URL(string: String(format: "%@%d", stagingTableUrl, i))!
            let table = AGSServiceFeatureTable(url: url)
            table.featureRequestMode = .onInteractionNoCache
            let layer = AGSFeatureLayer(featureTable: table)
            
            featureLayers.append(layer)
            allLayers.append(layer)

            layer.load(completion: { (error) in
                loadedCount = loadedCount + 1
                self.updateLoadingProgress(loadedCount, outOf: 27, withMessage: "Loading Layer")

                if loadedCount > 26 {
                    DispatchQueue.main.async {
                        self.addStagingLayers()
                    }
                }
                guard error == nil else {
                    print("Error loading layer ", i, ": ", error!.localizedDescription)
                    return
                }
            })
        }
    }‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

This method gets called during the viewDidLoad() event. An authentication challenge pops up for less than a second and disappears before I can enter credentials. Then the map fails to load the operational layers and the console displays "Unauthorized access" for each layer.

Here is a screenshot of the challenge:

 

It does not originate from the app. Is there a way to trigger this prompt in a more user friendly way?

0 Kudos
Nicholas-Furness
Esri Regular Contributor

Sorry for the slow reply.

It's odd that the credential prompt disappears. That shouldn't happen. I wonder if that's a side-effect of the SVProgressHUD? Is this still happening?

But to provide a different login panel (the one in your screenshot is the default one that Runtime provides), you make use of AGSAuthenticationManager.delegate and provide a custom challenge handler.

That challenge handler can display your own UI to get a username and password, which you can then create an AGSCredential from and pass on to the runtime. Here's an example (getLoginInfo() is your custom code that displays your own UI and returns a username and password - in this case I'm using a username of nil to signify that the user canceled).

import UIKit
import ArcGIS

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, AGSAuthenticationManagerDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        AGSAuthenticationManager.shared().delegate = self
        
        return true
    }
    
    func authenticationManager(_ authenticationManager: AGSAuthenticationManager, didReceive challenge: AGSAuthenticationChallenge) {
        // Show your UI
        getLoginInfo { (username, password) in
            if let username = username {
                let credential = AGSCredential(user: username, password: password)
                challenge.continue(with: credential)
            } else {
                challenge.cancel()
            }
        }
    }
...

You don't have to set your AppDelegate to be the AGSAuthenticationManager's delegate, but it's often convenient since it's around the lifetime of your app.