Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

Authenticator

The Authenticator is a ready-to-use component designed to simplify handling authentication challenges when working with the ArcGIS Maps SDK for Kotlin. It provides a user-friendly interface to manage various authentication scenarios, such as network and ArcGIS-specific authentication challenges, ensuring seamless integration into your app.

drawing

Features

The Authenticator is a Composable function that will display a prompt when it is asked to handle an authentication challenge. It can handle the following types of authentication:

  • ArcGIS Authentication (OAuth, Identity-Aware Proxy, and Token)
  • Integrated Windows Authentication (IWA)
  • Client Certificate (PKI)
  • Server Trust challenges

The Authenticator works directly with the ArcGISEnvironment.authenticationManager, so any configuration set on the AuthenticationManager (for example, credential persistence, interceptors, and the http cache) will be reflected in the Authenticator's behavior.

A second Composable component, the DialogAuthenticator, performs the same functionality as the Authenticator but presents Username/Password prompts and Server Trust prompts in a Dialog. In any of the code samples below, it is sufficient to replace Authenticator with DialogAuthenticator and the behavior will be identical.

Usage

View the API Reference for the authentication module here.

To see an example of how to use the Authenticator, try out the microapp here.

The Authenticator is designed to be displayed over the top of your app's UI. As such, it should be called at a near-root level, for example, at the same level as a NavHost; and it should be called at the bottom of the function, so it draws over other content. If you call other content after you call Authenticator(), that content will be displayed on top of it.

@Composable
fun MyApp() {
    val authenticatorState: AuthenticatorState = remember { AuthenticatorState() }
    MyAppContent()
    Authenticator(authenticatorState)
}

To enable OAuth or Identity-Aware Proxy (IAP) authentication in your app, configure the AuthenticatorState with the corresponding properties:

val oAuthUserConfiguration = OAuthUserConfiguration(
    "https://www.arcgis.com/",
    "<your-client-id>",
    "my-ags-app://auth"
)
val iapConfiguration = IapConfiguration.create("Your IAP configuration JSON file path").getOrThrow()

authenticatorState.oAuthUserConfigurations = listOf(oAuthUserConfiguration)
authenticatorState.iapConfigurations = listOf(iapConfiguration)

To properly handle redirect intents from the browser during OAuth sign-in or IAP sign-in/sign-out, you must declare the AuthenticationActivity in your app's manifest as follows:

<activity
    android:name="com.arcgismaps.toolkit.authentication.AuthenticationActivity"
    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:host="auth"
            android:scheme="my-ags-app" />
    </intent-filter>
</activity>

Note: If the OAuth user configuration is invalid, the Authenticator will not launch a browser page and will prompt for a username and password instead.

Intercepting Challenges

AuthenticatorState acts as the NetworkAuthenticationChallengeHandler and ArcGISAuthenticationChallengeHandler for your app by default. When you create it, you can choose not to set it as the default challenge handler if you wish:

class MyAppViewModel(application: Application) : AndroidViewModel(application), ArcGISAuthenticationChallengeHandler {

    val authenticatorState = AuthenticatorState(
        setAsArcGISAuthenticationChallengeHandler = false,
        setAsNetworkAuthenticationChallengeHandler = true
    )

    override suspend fun handleArcGISAuthenticationChallenge(challenge: ArcGISAuthenticationChallenge): ArcGISAuthenticationChallengeResponse {
        val shouldAuthenticatorHandleChallenge = someBusinessLogic(challenge)
        return if (shouldAuthenticatorHandleChallenge) {
            authenticatorState.handleArcGISAuthenticationChallenge(challenge)
        } else { 
            ArcGISAuthenticationChallengeResponse.ContinueAndFail
        }
    }
}

Intercepting Browser Authentication Challenges

The Authenticator launches a Custom Tab when an OAuth or Identity-Aware Proxy (IAP) challenge is issued. When the Custom Tab completes with a redirect url, it is received by the AuthenticationActivity that is declared in your app's manifest via its intent filter.

If you want to launch a Custom Tab from your own app's activity, follow these steps:

  1. Remove the AuthenticationActivity in your app's manifest and put its intent filter on the activity that you wish to receive the redirect intent

  2. Set your activity's launchMode to singleTop.

    ℹ️ This is necessary because, when handling OAuth or IAP redirects, the Android system delivers the redirect intent to your activity. Using singleTop ensures that if your activity is already running at the top of the stack, a new instance is not created; instead, the existing instance receives the intent via onNewIntent. This allows your app to properly process authentication results without launching duplicate activities.

    <activity
        android:name="<Your-App-Activity-Name>"
        ...
        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:host="auth"
                android:scheme="my-ags-app"/>
        </intent-filter>
    </activity>
  3. Call the extension function launchCustomTabs in the lambda onPendingBrowserAuthenticationChallenge of the Authenticator, passing in the pending BrowserAuthenticationChallenge:

    DialogAuthenticator(
        authenticatorState = authenticatorState,
        onPendingBrowserAuthenticationChallenge = { pendingBrowserAuthenticationChallenge ->
            launchCustomTabs(pendingBrowserAuthenticationChallenge)
        }
    )   
  4. Handle the redirect in your app activity's onNewIntent and onResume overrides:

    • You can check if the intent was caused by an OAuth or IAP redirect because the intent.data.toString() will start with your OAuth or IAP configuration's redirect URI.

    • Currently, IAP sign-out does not redirect back to the app, so you will not receive an intent in onNewIntent. Instead, this will need to be handled in onResume when the Custom Tab is closed. See documentation of AuthenticatorState.completeBrowserAuthenticationChallenge for more details.

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.data?.toString()?.let { redirectUri ->
            if (redirectUri.startsWith("your-redirect-uri")) {
                // This gets called when OAuth or IAP redirects back to the app.
                authenticationAppViewModel.authenticatorState.completeBrowserAuthenticationChallenge(intent)
            }
        }
    }
    
    override fun onResume() {
        super.onResume()
        // This gets called when the Custom Tab is closed using the close button or the phone's back button, so we
        // pass null.
        if (viewModel.isCustomTabLaunched()) {
            authenticationAppViewModel.authenticatorState.completeBrowserAuthenticationChallenge(null)
        }
    }

Using Private Browser Sessions (Ephemeral mode) for OAuth Authentication

To display OAuth sign-in prompts in a private browser session (ephemeral mode), set the preferPrivateWebBrowserSession property to true in your OAuthUserConfiguration. This ensures that the browser does not use any cached credentials, which is important for enterprise security.

authenticatorState.oAuthUserConfigurations = listOf(
   OAuthUserConfiguration(
        "https://www.arcgis.com/",
        "<your-client-id>",
        "my-ags-app://auth",
        preferPrivateWebBrowserSession = true
    )
)

Note: Private web browsing sessions (ephemeral mode) may not work in Chrome versions lower than 136, and support may vary across non-Chromium-based browsers. For best results, ensure your users have updated to a newer version of Chrome, and be aware that not all browsers may support this feature.

Signing out

The authentication toolkit library provides a signOut function on AuthenticatorState to clear stored credentials, revoke OAuth Tokens, and launch the browser to invalidate the Identity-Aware Proxy (IAP) session if any IAP credentials are present.

authenticatorState.signOut()