Learn how to authenticate a user to access a secure ArcGIS service with OAuth 2.0.
In this tutorial, you will build an app that uses named user login credentials to access a secure ArcGIS service using OAuth 2.0.
You can use different types of authentication to access ArcGIS location services. To implement OAuth 2.0, you can use your ArcGIS account to register an application 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 services that consume credits, the app user's account will be charged.
You will implement OAuth 2.0 so users can sign in to ArcGIS to access the ArcGIS World Traffic service.
Prerequisites
The following are required for this tutorial:
- An ArcGIS account to configure OAuth for your application.
- A development and deployment environment that meets the system requirements.
- An IDE for Android development in Kotlin.
Steps
Configure OAuth 2.0 for your app
Use the ArcGIS Developer dashboard to create an application, generate a client ID, and define a redirect URL to access secure services.
- Sign in to your ArcGIS developer account. If you don't already have one, sign-up for free. You need to sign in so you can create an application and get a client ID for authentication.
- Click the OAuth 2.0 tab in the ribbon at the top.
- Click the New Application button in the upper-left of the page.
- In the Create New Application window, provide a Name and an optional Description for your application definition. Then click Create application. When the application is created,
Client ID
,Client Secret
, andTemporary Token
values will also be generated. - Click the Add URI button at the bottom of the page to add a redirect URL.
- In the Add Allowed URI window, type
authenticate-with-oauth:
and click Add URI.//auth
client ID
and redirect URL
when implementing OAuth in your app's code.The client ID
uniquely identifies your app on the authenticating server. If the server cannot find an app with the provided client ID, it will not proceed with authentication.
The redirect URL
is used to identify a response from the authenticating server when the system returns control back to your app after an OAuth 2.0 login. You can configure several redirect URLs in your application definition and can remove or edit them. It's important to make sure the redirect URL used in your app's code matches a redirect URL configured for the application.
- The redirect URL consists of three parts:
- the scheme
- the separator string "://"
- the host
- Scheme: You are probably familiar with standard schemes used in internet URLs:
http
,https
,ftp
, etc. In OAuth 2.0, you can create a private-use URI scheme (also known as custom URL scheme), which will only be used by the authorization server and your app. This is an arbitrary string whose allowed characters are declared in the Uniform Resource Locators (URL) Spec . Though not strictly required, making your scheme unique is a good practice. Some organization use the app ID or the reverse domain of their company (com.mycompany.appname) as the URL scheme. - Host: An arbitrary string occupying the host portion of the URL. It allows you to use the same URL scheme with different URL hosts.
A temporary token
can be used to test access to secure resources without having to implement the full OAuth workflow.
The client secret
is only needed in some OAuth workflows and will not be used in this tutorial.
Open an Android Studio project with Gradle
-
To start this tutorial, complete the Display a map tutorial. Or download and unzip the Display a map solution in a new folder.
-
Modify the old project for use in this new tutorial. Expand More info for instructions.
-
On your file system, delete the .idea folder, if present, at the top level of your project.
-
In the Android tool window, open app > res > values > strings.xml.
In the
<string name="app_
element, change the text content to Access services with OAuth 2.0.name"> strings.xmlUse dark colors for code blocks <resources> <string name="app_name">Access services with OAuth 2.0</string> <string name="oauth_client_id">YOUR_CLIENT_ID</string> <string name="oauth_redirect_uri_scheme">authenticate-with-oauth</string> <string name="oauth_redirect_uri_host">auth</string> </resources>
-
In the Android tool window, open Gradle Scripts > settings.gradle.kts.
Change the value of
root
to "Access services with OAuth 2.0".Project.name settings.gradle.ktsUse dark colors for code blocks dependencyResolutionManagement { @Suppress("UnstableApiUsage") repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) @Suppress("UnstableApiUsage") repositories { google() mavenCentral() maven { url = uri("https://esri.jfrog.io/artifactory/arcgis") } } } rootProject.name = "Access services with OAuth 2.0" include(":app")
-
The UI theme composable in Display a map tutorial was
Display
. Rename the theme composable throughout the tutorial by refactoringAMap Theme Display
.AMap Theme In the Android tool window, open app > kotlin+java > com.exmple.app > ui.theme > Theme.kt.
Right-click the function name
Display
and select Refactor -> Rename. Replace the name withAMap Theme Access
.Services With OAuth Theme Theme.ktUse dark colors for code blocks Copy @Composable fun DisplayAMapTheme( darkTheme: Boolean = isSystemInDarkTheme(), // Dynamic color is available on Android 12+ dynamicColor: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { val context = LocalContext.current if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) } darkTheme -> DarkColorScheme else -> LightColorScheme }
-
Click File > Sync Project with Gradle files. Android Studio will recognize your changes and create a new .idea folder.
-
-
Delete the code that sets your API key. Since your app will be using OAuth, you will not need an API Key.
MainActivity.ktUse dark colors for code blocks Remove line Remove line Remove line Remove line Remove line Remove line class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setApiKey() setContent { DisplayAMapTheme { MainScreen() } } } private fun setApiKey() { ArcGISEnvironment.apiKey = ApiKey.create("YOUR_API_KEY") } }
Prepare files before coding the app
Modify the files from the Display a map tutorial for use in this tutorial: you will replace import statements, define strings for OAuth client ID, redirect URI scheme, redirect URI HTMLOListElement, and add an Android activity for OAuth user sign-in.
-
In the Android tool window, open Gradle Scripts > build.gradle.kts (Module: app) and add a dependency on the
authentication
component defined in the ArcGIS Maps SDK for Kotlin Toolkit.build.gradle.kts (:app)Use dark colors for code blocks dependencies { implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0") implementation("androidx.activity:activity-compose:1.8.2") // Jetpack Compose Bill of Materials implementation(platform("androidx.compose:compose-bom:2023.10.01")) // Jetpack Compose dependencies implementation("androidx.compose.ui:ui") implementation("androidx.compose.material3:material3") // ArcGIS Map Kotlin SDK dependencies implementation("com.esri:arcgis-maps-kotlin:200.4.0") // Toolkit dependencies implementation(platform("com.esri:arcgis-maps-kotlin-toolkit-bom:200.4.0")) implementation("com.esri:arcgis-maps-kotlin-toolkit-geoview-compose") implementation("com.esri:arcgis-maps-kotlin-toolkit-authentication") }
-
Open app > kotlin+java > com.example.app > MainScreen.kt. Replace the import statements with the imports needed for this tutorial.
MainScreen.ktUse dark colors for code blocks @file:OptIn(ExperimentalMaterial3Api::class) package com.example.app.screens import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.arcgismaps.httpcore.authentication.OAuthUserConfiguration import com.arcgismaps.mapping.ArcGISMap import com.arcgismaps.mapping.BasemapStyle import com.arcgismaps.mapping.Viewpoint import com.arcgismaps.mapping.layers.ArcGISMapImageLayer import com.arcgismaps.toolkit.authentication.Authenticator import com.arcgismaps.toolkit.authentication.AuthenticatorState import com.arcgismaps.toolkit.geoviewcompose.MapView import com.example.app.R
-
Open app > res > values > strings.xml. Enter certain values that you specified while creating your OAuth configuration in Configure OAuth 2.0 for your app above.
To find these values, log in to the Dashboard with your developer account, click the OAuth 2.0 tab, scroll to the OAuth configuration you created (It will have a name such as "Access Services OAuth2".) and click View Full Credentials.
In string.xml define strings for the OAuth client ID, the redirect URI scheme, and the redirect URI host. Values are not quoted.
-
oauth_client_id: Replace the placeholder YOUR_CLIENT_ID with the Client ID that you defined for your app.
-
oauth_redirect_uri_scheme: The scheme of the redirect URI you defined for your app. This value is
authenticate-with-oauth
if you followed the suggestions in Configure OAuth 2.0 for your app. -
oauth_redirect_uri_host: The host of the redirect URI that you defined for your app. This value is
auth
if you followed the suggestions in Configure OAuth 2.0 for your app.
strings.xmlUse dark colors for code blocks <resources> <string name="app_name">Access services with OAuth 2.0</string> <string name="oauth_client_id">YOUR_CLIENT_ID</string> <string name="oauth_redirect_uri_scheme">authenticate-with-oauth</string> <string name="oauth_redirect_uri_host">auth</string> </resources>
-
-
Open app > manifests > AndroidManifest.xml. Add an
<activity>
tag that declares the OAuth user sign-in activity. Note the<data>
element that references the Redirect URI scheme and host.The
OAuthUserSignInActivity
class is an activity that launches a CustomTabs activity and receives the redirect intent that is sent when the user completes the CustomTabs prompt. For more information, see theREADME
for theauthentication
component in the ArcGIS Maps SDK for Kotlin Toolkit.AndroidManifest.xmlUse dark colors for code blocks <activity android:name="com.arcgismaps.toolkit.authentication.OAuthUserSignInActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize" android:exported="true" android:launchMode="singleTop" > <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="@string/oauth_redirect_uri_scheme" android:host="@string/oauth_redirect_uri_host" /> </intent-filter> </activity>
Implement OAuth 2.0 authentication
The composable function Authenticator
abstracts some details of OAuth 2.0 authentication in your app.
Calling AuthenticatorState
requires that you pass an AuthenticatorState
, which has an o
property. Use your OAuth client ID and redirect URI to instantiate OAuthUserConfiguration
.
The Authenticator
composable displays an Authentication UI when an authentication challenge is issued. The Authenticator
interface handles authentication challenges and exposes the state for the Authenticator
to display to the user. Both are defined in the authentication
component of the ArcGIS Maps SDK for Kotlin Toolkit.
For more information, go to the ArcGIS Maps SDK for Kotlin Toolkit. Consult the README
for the authentication
component, and examine the source code for Authenticator
and Authenticator
.
-
Create
o
,Auth Client I d o
, andAuth Redirect U r i Scheme o
variables, using string resources defined inAuth Redirect U r i Host strings.xml
.MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val oAuthClientId = stringResource(id = R.string.oauth_client_id) val oAuthRedirectUriScheme = stringResource(id = R.string.oauth_redirect_uri_scheme) val oAuthRedirectUriHost = stringResource(id = R.string.oauth_redirect_uri_host) val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map ) } }
-
Create a variable named
o
that is initialized as the string concatenation of the Redirect URI scheme, the separator "://", and the Redirect URI host.Auth Redirect Uri MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val oAuthClientId = stringResource(id = R.string.oauth_client_id) val oAuthRedirectUriScheme = stringResource(id = R.string.oauth_redirect_uri_scheme) val oAuthRedirectUriHost = stringResource(id = R.string.oauth_redirect_uri_host) val oAuthRedirectUri = oAuthRedirectUriScheme + "://" + oAuthRedirectUriHost val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map ) } }
-
Inside a
remember
block, create an instance ofAuthenticatorState
, which is from theauthentication
component of ArcGIS Maps SDK for Kotlin Toolkit. Then set theo
property by instantiatingAuth User Configuration OAuthUserConfiguration
and passingportal
,Url client
, andI d redirect
. Last, assign theURL remember { }
call to a local variable namedauthenticator
.State MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val oAuthClientId = stringResource(id = R.string.oauth_client_id) val oAuthRedirectUriScheme = stringResource(id = R.string.oauth_redirect_uri_scheme) val oAuthRedirectUriHost = stringResource(id = R.string.oauth_redirect_uri_host) val oAuthRedirectUri = oAuthRedirectUriScheme + "://" + oAuthRedirectUriHost val authenticatorState = remember { AuthenticatorState().apply { oAuthUserConfiguration = OAuthUserConfiguration( portalUrl = "https://www.arcgis.com", clientId = oAuthClientId, redirectUrl = oAuthRedirectUri ) } } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map ) } }
-
Call the
Authenticator
composable, passingauthenticator
. TheState Authenticator
composable function should be called from the root level of your UI, typically from youMain
, to ensure that authentication prompts always appear on top of any other UI of your application.Screen MainScreen.ktUse dark colors for code blocks @Composable fun MainScreen() { val oAuthClientId = stringResource(id = R.string.oauth_client_id) val oAuthRedirectUriScheme = stringResource(id = R.string.oauth_redirect_uri_scheme) val oAuthRedirectUriHost = stringResource(id = R.string.oauth_redirect_uri_host) val oAuthRedirectUri = oAuthRedirectUriScheme + "://" + oAuthRedirectUriHost val authenticatorState = remember { AuthenticatorState().apply { oAuthUserConfiguration = OAuthUserConfiguration( portalUrl = "https://www.arcgis.com", clientId = oAuthClientId, redirectUrl = oAuthRedirectUri ) } } val map = remember { createMap() } Scaffold( topBar = { TopAppBar(title = { Text(text = stringResource(id = R.string.app_name)) }) } ) { MapView( modifier = Modifier.fillMaxSize().padding(it), arcGISMap = map ) } Authenticator(authenticatorState = authenticatorState) }
Add a traffic layer
You will add a layer to display the ArcGIS World Traffic service, a dynamic map service that presents historical and near real-time traffic information for different regions in the world. This is a secure service and requires an ArcGIS Online organizational subscription.
ArcGIS World Traffic service data is updated every five minutes to provide traffic speed and traffic incident visualization and identification.
Traffic speeds are displayed as a percentage of free-flow speeds, which is frequently the speed limit or how fast cars tend to travel when unencumbered by other vehicles. The streets are color coded as follows:
- Green (fast): 85 - 100% of free flow speeds
- Yellow (moderate): 65 - 85%
- Orange (slow); 45 - 65%
- Red (stop and go): 0 - 45%
-
Create an
ArcGISMapImageLayer
to display the traffic service.MainScreen.ktUse dark colors for code blocks fun createMap(): ArcGISMap { // Create a layer to display the ArcGIS World Traffic service val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer") return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.02700, longitude = -118.80543, scale = 72000.0 ) operationalLayers.add(trafficLayer) } }
-
Add the layer to the map's collection of data layers (operational layers).
MainScreen.ktUse dark colors for code blocks fun createMap(): ArcGISMap { // Create a layer to display the ArcGIS World Traffic service val trafficLayer = ArcGISMapImageLayer("https://traffic.arcgis.com/arcgis/rest/services/World/Traffic/MapServer") return ArcGISMap(BasemapStyle.ArcGISTopographic).apply { initialViewpoint = Viewpoint( latitude = 34.02700, longitude = -118.80543, scale = 72000.0 ) operationalLayers.add(trafficLayer) } }
-
Click Run > Run > app to run the app.
When running your app, you will be prompted for username and password in an Android Custom Tab.
After successfully logging in with your ArcGIS Online credentials, you should see the map with the topographic basemap layer centered on the Santa Monica Mountains in California. You will also see the traffic layer, with its symbology of green, yellow, orange, and red roads to indicate current traffic flow. This is a secured layer, which is visible in your app because the user has entered valid ArcGIS Online username and password.