--- title: "Authentication State" slug: "authentication-state" updated: 2026-04-28T03:48:11Z published: 2026-04-28T03:48:11Z canonical: "ignite.ticketmaster.com/authentication-state" --- > ## Documentation Index > Fetch the complete documentation index at: https://ignite.ticketmaster.com/llms.txt > Use this file to discover all available pages before exploring further. # Authentication State ## Authentication Analytics The Authentication SDK provides callbacks so you can track authentication state changes — configuration, login, token refresh, and logout. You can use these to update your UI, forward events to your analytics platform, or trigger downstream logic. Both iOS and Android expose a state machine with nearly identical states. The key difference is the delivery mechanism: iOS uses a delegate protocol, while Android uses `LiveData`. --- ## Overview | Aspect | iOS | Android | | --- | --- | --- | | **State API** | `TMAuthenticationDelegate` protocol | `TMAuthentication.getLoginStateLiveData()` | | **Delivery** | Delegate callback with `ServiceState` enum | `LiveData<ServiceLoginState>` observer | | **State enum** | `TMAuthentication.ServiceState` (14 states) | `ServiceLoginState` (15 states — includes `LOGIN_EXCHANGING`) | | **Backend context** | `BackendService?` parameter on each callback | Not provided per-state; query with `getToken(AuthSource)` | | **Error context** | `Error?` parameter on each callback | Not provided per-state; use `ActivityResult` for login outcome | | **Federated login details** | States only (e.g., `loginLinkAccountPresented`) | States + `UserAnalyticsDelegate` events for button-level tracking | --- ## Service States Both platforms share the same state flow. States are grouped into four phases. ### Configuration States These fire during SDK initialization (`TMAuthentication.Builder.build()`) before any user interaction. During configuration, multiple backend services (Host, Archtics, MicroFlex) may be configured, causing multiple `serviceConfigured` callbacks — one per backend. `serviceConfigurationCompleted` fires only once when all backends have finished configuring. | iOS (`ServiceState`) | Android (`ServiceLoginState`) | Description | | --- | --- | --- | | `serviceConfigurationStarted` | `SERVICE_CONFIGURATION_STARTED` | Configuration has started. | | `serviceConfigured` | `SERVICE_CONFIGURED` | A specific backend has been configured. May fire multiple times. | | `serviceConfigurationCompleted` | `SERVICE_CONFIGURATION_COMPLETED` | All backends have finished configuring. The SDK is ready for login. | **Querying configured services:** You can check which services were configured at any time: **iOS** ```swift // Check which backends are configured via TMAuthentication.BackendService // The backend parameter in onStateChanged tells you which service was configured ``` **Android** ```kotlin // Check which backends are configured via the configuration object val config = tmAuthentication.configuration val hasHost = config.modernAccounts?.host != null val hasArchtics = config.modernAccounts?.archtics != null val hasMfx = config.modernAccounts?.mfx != null ``` ### Login States These fire during the login flow. During login, multiple services may be logged in (for example, Archtics and Host). The `loggedIn` state may fire multiple times — once per backend service. `loginCompleted` fires only once when the entire login process (including link-account flows) has finished. | iOS (`ServiceState`) | Android (`ServiceLoginState`) | Description | | --- | --- | --- | | `loginStarted` | `LOGIN_STARTED` | Login has started. | | — | `LOGIN_EXCHANGING` | *(Android only)* Token exchange is in progress. | | `loginPresented` | `LOGIN_PRESENTED` | A login page has been presented for a backend. | | `loggedIn` | `LOGGED_IN` | The user has logged in to a specific backend. May fire multiple times. | | `loginAborted` | `LOGIN_ABORTED` | The user manually cancelled the login. | | `loginFailed` | `LOGIN_FAILED` | Login failed for a specific backend. | | `loginLinkAccountPresented` | `LOGIN_LINK_ACCOUNT_PRESENTED` | A link-account prompt has been presented. | | `loginCompleted` | `LOGIN_COMPLETED` | The entire login process has completed. | **Querying logged-in services:** **iOS** ```swift // The backend parameter in onStateChanged tells you which service was logged in ``` **Android** ```kotlin // Check which backends have valid tokens AuthSource.entries.forEach { source ->    val token = tmAuthentication.getToken(source)    if (token != null) {        Log.d("Auth", "$source is logged in")    } } ``` ### Token Refresh States | iOS (`ServiceState`) | Android (`ServiceLoginState`) | Description | | --- | --- | --- | | `tokenRefreshed` | `TOKEN_REFRESHED` | The user's auth token has been refreshed. May fire multiple times during the app's lifetime. | ### Logout States When you call logout, by default the system logs out of **all** currently configured services. This means the `loggedOut` state may fire multiple times — once per backend. `logoutCompleted` fires only once at the end. | iOS (`ServiceState`) | Android (`ServiceLoginState`) | Description | | --- | --- | --- | | `logoutStarted` | `LOGOUT_STARTED` | Logout has started. | | `loggedOut` | `LOGGED_OUT` | The user has logged out of a specific backend. May fire multiple times. | | `logoutCompleted` | `LOGOUT_COMPLETED` | The entire logout process has completed. | --- ## Setup ### iOS Set yourself as the delegate on the shared `TMAuthentication` instance: ```swift TMAuthentication.shared.delegate = self ``` Then conform to `TMAuthenticationDelegate`: ```swift extension MyViewController: TMAuthenticationDelegate {    func onStateChanged(        backend: TMAuthentication.BackendService?,        state: TMAuthentication.ServiceState,        error: (Error)?) {        // Handle state change    } } ``` **Parameters:** | Name | Type | Description | | --- | --- | --- | | `backend` | `TMAuthentication.BackendService?` | The backend service (Host, Archtics, etc.) the state relates to. `nil` for global state changes. | | `state` | `TMAuthentication.ServiceState` | The current authentication state. | | `error` | `Error?` | An error, if one occurred during the state transition. | > [!WARNING] > **Note:** Always include an `@unknown default` case to handle new states that may be added in future SDK versions. ### Android Observe the login state `LiveData` from your `TMAuthentication` instance: ```kotlin tmAuthentication.getLoginStateLiveData().observe(this) { state ->    Log.d("Auth", "State changed: $state") } ``` Android also provides login outcomes via `ActivityResult`: ```kotlin private val loginLauncher = registerForActivityResult(    ActivityResultContracts.StartActivityForResult() ) { result ->    when (result.resultCode) {        Activity.RESULT_OK -> handleLoginSuccess()        Activity.RESULT_CANCELED -> handleLoginCancelled()    } } ``` > [!NOTE] > **Note:** Use both mechanisms together — `getLoginStateLiveData()` gives you granular state transitions, while `ActivityResult` gives you the final login outcome. --- ## Federated Login Events (Android) In addition to the `ServiceLoginState` state machine, the Android SDK emits button-level federated login analytics through `UserAnalyticsDelegate`. These provide more detail about user interactions during the link-account flow: | Action | Description | | --- | --- | | `ACTION_FED_LOGIN_LINK_ACCOUNTS_SCREEN_SHOWED` | Link-accounts screen was presented | | `ACTION_FED_LOGIN_LINK_ACCOUNTS_SCREEN_DISMISSED` | Link-accounts screen was dismissed | | `ACTION_FED_LOGIN_SCREEN_DISMISSED_AFTER_SUCCESS_LOGIN_NO_LINK` | Login dismissed after success without linking | | `ACTION_FED_LOGIN_LINK_ACCOUNTS_BUTTON_PRESSED` | User pressed "Link Accounts" | | `ACTION_FED_LOGIN_NO_THANKS_BUTTON_PRESSED` | User pressed "No Thanks" | > [!NOTE] > **Note:** These `actionName` strings are prefixed with `com.ticketmaster.presencesdk.eventanalytic.action.`. ```kotlin UserAnalyticsDelegate.handler.observe(this) { data ->    data?.let {        if (it.actionName.contains("FED_LOGIN")) {            Log.d("AuthAnalytics", "Federated login event: ${it.actionName}")        }    } } ``` --- ## Session Expiration ### iOS Session expiration is handled via `TMTicketsOrderDelegate`. See [Analytics - Actions](/v1/docs/analytics-4#user-actions) for details. ### Android Observe session expiration (401 errors) via the `sessionExpiredDelegate` Flow: ```kotlin lifecycleScope.launch {    TicketsSDKSingleton.sessionExpiredDelegate.collect {        Log.d("Auth", "Session expired")        TicketsSDKSingleton.logout {            launchLogin()        }    } } ``` --- ## Complete Examples ### iOS ```swift TMAuthentication.shared.delegate = self extension MyViewController: TMAuthenticationDelegate {    func onStateChanged(        backend: TMAuthentication.BackendService?,        state: TMAuthentication.ServiceState,        error: (Error)?) {        if let backend = backend {            if let error = error {                print("Auth: .\(state.rawValue) backend: \(backend.description) error: \(error.localizedDescription)")            } else {                print("Auth: .\(state.rawValue) backend: \(backend.description)")            }        } else {            if let error = error {                print("Auth: .\(state.rawValue) error: \(error.localizedDescription)")            } else {                print("Auth: .\(state.rawValue)")            }        }        switch state {        // Configuration        case .serviceConfigurationStarted:            break        case .serviceConfigured:            break        case .serviceConfigurationCompleted:            // SDK is ready — you can now trigger login            break        // Login        case .loginStarted:            break        case .loginPresented:            break        case .loggedIn:            break        case .loginAborted:            break        case .loginFailed:            if let error = error {                print("Login failed: \(error.localizedDescription)")            }        case .loginLinkAccountPresented:            break        case .loginCompleted:            // Login fully complete — safe to update UI            break        // Token        case .tokenRefreshed:            break        // Logout        case .logoutStarted:            break        case .loggedOut:            break        case .logoutCompleted:            // Logout fully complete — update UI            break        @unknown default:            break        }    } } ``` ### Android ```kotlin class MainActivity : AppCompatActivity() {    private lateinit var tmAuthentication: TMAuthentication    private val loginLauncher = registerForActivityResult(        ActivityResultContracts.StartActivityForResult()    ) { result ->        when (result.resultCode) {            Activity.RESULT_OK -> showEventsScreen()            Activity.RESULT_CANCELED -> Log.d("Auth", "Login cancelled or failed")        }    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setupSDK()    }    private fun setupSDK() {        lifecycleScope.launch {            TMAuthentication.Builder(                apiKey = BuildConfig.CONSUMER_KEY,                clientName = BuildConfig.TEAM_NAME            )                .region(TMXDeploymentRegion.US)                .environment(TMXDeploymentEnvironment.Production)                .build(this@MainActivity)                .fold(                    onSuccess = { auth ->                        tmAuthentication = auth                        observeAuthState()                    },                    onFailure = { error ->                        Log.e("Auth", "Configuration failed: ${error.message}")                    }                )        }    }    private fun observeAuthState() {        // Observe granular state transitions        tmAuthentication.getLoginStateLiveData().observe(this) { state ->            Log.d("Auth", "State: $state")            when (state) {                ServiceLoginState.SERVICE_CONFIGURATION_COMPLETED -> {                    // SDK is ready — launch login                    launchLogin()                }                ServiceLoginState.LOGIN_COMPLETED -> {                    // Login fully complete                    showEventsScreen()                }                ServiceLoginState.LOGIN_FAILED -> {                    Log.e("Auth", "Login failed")                }                ServiceLoginState.LOGIN_LINK_ACCOUNT_PRESENTED -> {                    Log.d("Auth", "Link account screen presented")                }                ServiceLoginState.LOGOUT_COMPLETED -> {                    // Logout complete — return to login screen                    launchLogin()                }                else -> { }            }        }    }    private fun launchLogin() {        loginLauncher.launch(tmAuthentication.getLoginIntent(this))    }    private fun showEventsScreen() {        TicketsSDKSingleton.getEventsFragment(this)?.let { fragment ->            supportFragmentManager.beginTransaction()                .replace(R.id.fragmentContainer, fragment)                .commit()        }    } } ``` --- ## Common Patterns ### Waiting for Login to Complete On both platforms, wait for `loginCompleted` / `LOGIN_COMPLETED` before updating your UI. The `loggedIn` / `LOGGED_IN` state fires per-backend and login may still be in progress. ### Handling Multiple Backends During login with multiple backends (e.g., Host + Archtics), you will see a sequence like: ```plaintext LOGIN_STARTED LOGIN_PRESENTED     (for Archtics) LOGGED_IN           (for Archtics) LOGIN_PRESENTED     (for Host) LOGGED_IN           (for Host) LOGIN_COMPLETED     (once, at the end) ``` The same pattern applies to configuration and logout — intermediate states fire per-backend, while the `*_COMPLETED` state fires once at the end. --- ## Related Documentation - [Authentication](/v1/docs/authentication-1) — Authentication setup and configuration - [Configuration](/v1/docs/configuration-1) — SDK configuration guide