Access services with OAuth credentials

Learn how to implement user authentication to access a secure ArcGIS service with OAuth credentials.

access services with oauth 2

You can use different types of authentication to access secured ArcGIS services. To implement OAuth credentials for user authentication, you can use your ArcGIS account to register an app with your portal and get a Client ID, and then configure your app to redirect users to login with their credentials when the service or content is accessed. This is known as user authentication. If the app uses premium ArcGIS Online services that consume credits, for example, the app user's account will be charged.

In this tutorial, you will build an app that implements user authentication using OAuth credentials so users can sign in and be authenticated through ArcGIS Online to access the ArcGIS World Traffic service.

Prerequisites

Before starting this tutorial:

  1. You need an ArcGIS Location Platform or ArcGIS Online account.

  2. Your system meets the system requirements.

Steps

Create OAuth credentials for user authentication

OAuth credentials are required to implement user authentication. These credentials are created as an Application item in your organization's portal.

  1. Go to the Create OAuth credentials for user authentication tutorial and create OAuth credentials using your ArcGIS Location Platform or ArcGIS Online account.

  2. Copy the Client ID and Redirect URL as you will use them to implement user authentication in the next step. The Client ID is found on the Application item's Overview page, while the Redirect URL is found on the Settings page.

Open the Xcode project

  1. To start the tutorial, complete the Display a map tutorial or download and unzip the solution.

  2. Open the .xcodeproj file in Xcode.

Remove API key

An API Key access token is not required for this app because you are implementing user authentication using OAuth 2.0 protocol.

  1. Open MainApp.swift and remove the line that sets the API key access token.

    MainApp.swift
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    import SwiftUI
    
    import ArcGIS
    
    @main
    struct MainApp: App {
    
        init() {
            ArcGISEnvironment.apiKey = APIKey("<#YOUR-ACCESS-TOKEN#>")
        }
    
        var body: some SwiftUI.Scene {
            WindowGroup {
                ContentView()
    
                    .ignoresSafeArea()
    
            }
        }
    
    }

Add a layer to map

Add the World Traffic layer to the map and test run the app.

  1. Open ContentView.swift and update the existing map variable to add a traffic layer to the map's operational layers collection.

    ContentView.swift
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    struct ContentView: View {
    
        @State private var map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(latitude: 34.02700, longitude: -118.80500, scale: 72_000)
    
            let trafficLayerURL = URL(string: "http://www.arcgis.com/home/item.html?id=bbdcd78953e5439985004023c8eda03d")!
            let trafficLayer = ArcGISMapImageLayer(url: trafficLayerURL)
            map.addOperationalLayer(trafficLayer)
    
            return map
        }()
    
        var body: some View {
            MapView(map: map)
    
        }
    
    }
    
  2. Press Command+R to run the app.

Note that the basemap and traffic layer do not load into the map because they both require authentication. Let's integrate OAuth authentication into the app.

Integrate OAuth credentials into your app

Create an ArcGISAuthenticationChallengeHandler that handles OAuth authentication every time a secured ArcGIS resource is accessed.

  1. Create a final class named MyArcGISChallengeHandler of type ArcGISAuthenticationChallengeHandler and tag it as the @MainActor. Initialize the challenge handler with an array of OAuthUserConfiguration.

    ContentView.swift
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    import SwiftUI
    
    import ArcGIS
    
    struct ContentView: View {
    
        @State private var map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(latitude: 34.02700, longitude: -118.80500, scale: 72_000)
    
            let trafficLayerURL = URL(string: "http://www.arcgis.com/home/item.html?id=bbdcd78953e5439985004023c8eda03d")!
            let trafficLayer = ArcGISMapImageLayer(url: trafficLayerURL)
            map.addOperationalLayer(trafficLayer)
    
            return map
        }()
    
        var body: some View {
            MapView(map: map)
    
        }
    
    }
    
    // Creates an ArcGIS authentication challenge handler.
    @MainActor
    final class MyArcGISChallengeHandler: ObservableObject, ArcGISAuthenticationChallengeHandler {
        // The OAuth configurations that this challenge handler can work with.
        let oAuthUserConfigurations: [OAuthUserConfiguration]
        public init(
            oAuthUserConfigurations: [OAuthUserConfiguration] = []
        ) {
            self.oAuthUserConfigurations = oAuthUserConfigurations
        }
    
    }
  2. Add a new function named handleArcGISAuthenticationChallenge(_:) to handle the authentication challenge. This function attempts to create an OAuthUserCredential using the credentials provided by the first OAuthUserConfiguration in the array. The credentials are stored in the in-memory credential store.

    ContentView.swift
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    // Creates an ArcGIS authentication challenge handler.
    @MainActor
    final class MyArcGISChallengeHandler: ObservableObject, ArcGISAuthenticationChallengeHandler {
        // The OAuth configurations that this challenge handler can work with.
        let oAuthUserConfigurations: [OAuthUserConfiguration]
        public init(
            oAuthUserConfigurations: [OAuthUserConfiguration] = []
        ) {
            self.oAuthUserConfigurations = oAuthUserConfigurations
        }
    
        // Handles the challenge to an ArcGIS secured resource that requires OAuth or ArcGIS Token authentication.
        func handleArcGISAuthenticationChallenge(
            _ challenge: ArcGISAuthenticationChallenge
        ) async throws -> ArcGISAuthenticationChallenge.Disposition {
            // If an OAuth user configuration is available for the challenge then create an `OAuthUserCredential`.
            if let configuration = oAuthUserConfigurations.first(where: { $0.canBeUsed(for: challenge.requestURL) }) {
                return .continueWithCredential(
                    try await OAuthUserCredential.credential(for: configuration)
                )
            } else {
                // If not, prompt the user for a username and password to create a `TokenCredential`.
                // ...
                return .continueWithCredential(
                    try await TokenCredential.credential(for: challenge, username: "username", password: "password")
                )
            }
        }
    
    }
  3. Create a @StateObject variable in ContentView named myArcGISChallengeHandler that creates an instance of the MyArcGISChallengeHandler class with the portal URL, client ID, and redirect URL. The client ID and redirect URL are values that you created in the previous step, Create OAuth credentials for user authentication.

    ContentView.swift
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    struct ContentView: View {
    
        /// `clientID` is a unique identifier made for a public application created by the ArcGIS Maps SDK team.
        /// `redirectURL` is the redirect URL registered with the clientID.
        /// - Note: The clientID and redirectURL values here are examples. You must register your own app, generate a client ID, and set a redirect URL to access secure services in your deployed app.
        @StateObject private var myArcGISChallengeHandler = MyArcGISChallengeHandler(
            oAuthUserConfigurations: [
                OAuthUserConfiguration(
                    portalURL: URL(string: "https://www.arcgis.com")!,
                    clientID: "lgAdHkYZYlwwfAhC",
                    redirectURL: URL(string: "my-ags-app://auth")!
                )
            ]
        )
    
        @State private var map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(latitude: 34.02700, longitude: -118.80500, scale: 72_000)
    
            let trafficLayerURL = URL(string: "http://www.arcgis.com/home/item.html?id=bbdcd78953e5439985004023c8eda03d")!
            let trafficLayer = ArcGISMapImageLayer(url: trafficLayerURL)
            map.addOperationalLayer(trafficLayer)
    
            return map
        }()
    
        var body: some View {
            MapView(map: map)
    
        }
    
    }
    
  4. Lastly, add an .onAppear modifier to the map view and assign myArcGISChallengeHandler to the ArcGISAuthenticationChallengeHandler that is provided by the app's ArcGISEnvironment.authenticationManager.

    ContentView.swift
    Expand
    Use dark colors for code blocks
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    struct ContentView: View {
    
        /// `clientID` is a unique identifier made for a public application created by the ArcGIS Maps SDK team.
        /// `redirectURL` is the redirect URL registered with the clientID.
        /// - Note: The clientID and redirectURL values here are examples. You must register your own app, generate a client ID, and set a redirect URL to access secure services in your deployed app.
        @StateObject private var myArcGISChallengeHandler = MyArcGISChallengeHandler(
            oAuthUserConfigurations: [
                OAuthUserConfiguration(
                    portalURL: URL(string: "https://www.arcgis.com")!,
                    clientID: "lgAdHkYZYlwwfAhC",
                    redirectURL: URL(string: "my-ags-app://auth")!
                )
            ]
        )
    
        @State private var map = {
            let map = Map(basemapStyle: .arcGISTopographic)
            map.initialViewpoint = Viewpoint(latitude: 34.02700, longitude: -118.80500, scale: 72_000)
    
            let trafficLayerURL = URL(string: "http://www.arcgis.com/home/item.html?id=bbdcd78953e5439985004023c8eda03d")!
            let trafficLayer = ArcGISMapImageLayer(url: trafficLayerURL)
            map.addOperationalLayer(trafficLayer)
    
            return map
        }()
    
        var body: some View {
            MapView(map: map)
    
                .onAppear {
                    ArcGISEnvironment.authenticationManager.arcGISAuthenticationChallengeHandler = myArcGISChallengeHandler
                }
    
        }
    
    }
    
    Expand
  5. Press Command + R to run the app.

Upon app launch, you will be prompted to log in with your ArcGIS Developer credentials. Once you authenticate successfully with ArcGIS Online, the basemap and traffic layer will appear in the map.

What's next?

Learn how to use additional API features, ArcGIS location services, and ArcGIS tools in these tutorials:

Your browser is no longer supported. Please upgrade your browser for the best experience. See our browser deprecation post for more details.