Post-login account security is often the feature most mobile teams plan to build and keep pushing to the next sprint. Setting up MFA, managing passkeys, generating recovery codes — it matters enormously to your users’ security posture, but the implementation is weeks of work: custom UIs on top of raw APIs, multi-step enrollment flows, step-up authentication, and the ever-present problem of keeping it all consistent with your app’s design system.
Auth0 Universal Components helped solve this for the web. Today, that same capability comes natively to Android.
Auth0 Universal Native Components for Android is a Jetpack Compose library that lets you drop a production-ready account security experience directly into your Android app. With a single composable, your users can enroll in TOTP, push notifications, SMS/email OTP, and passkeys, manage their enrolled methods, and generate recovery codes — all without leaving your app or seeing a browser redirect.
In this post, you will learn how you can clone a complete sample app, connect it to your Auth0 tenant, and have a fully functioning native account security experience running on your device — no UI code to write, no enrollment flows to implement.
Auth0 Universal Native Components on Android
The centerpiece of the SDK is a single composable: AuthenticatorSettingsComponent(). It can handle the entire account security lifecycle (enrollment, verification, and removal) for the following authentication factor types:
- TOTP: Authenticator app with QR code scanning
- Push notifications: Secure push-based authentication
- SMS OTP: Phone number verification codes
- Email OTP: Email verification codes
- Recovery codes: Backup codes for account recovery
- Passkeys: FIDO2/WebAuthn biometric enrollment (Android 14+)
Internal navigation between flows is managed by the library. There is no NavHost to configure, enrollment state machine to build, or step-up auth errors to handle. You drop in the composable and the library handles everything else.
Auth0 Universal Native Components is built entirely in Jetpack Compose — not a web view inside a native container. That distinction matters. Native components respect platform conventions: gesture navigation, system back handling, dark mode, dynamic type scaling, and accessibility all work correctly out of the box.
Prerequisites
Before you start, you will need:
- An Auth0 account with My Account APIs enabled on your tenant.
- A custom domain configured on your Auth0 tenant (required for passkeys, see below)
- Android Studio Meerkat | 2024.3.1 or later
- An Android device or emulator running Android 11+ (API 30+); passkey enrollment requires Android 14+ (API 34+)
Get the Sample App
Clone the sample app and open it in Android Studio:
git clone https://github.com/auth0-blog/auth0-universal-components-android cd auth0-universal-components-android
Open the project in Android Studio and let Gradle sync complete.
The app is fully wired up. Auth0 login, session management, and AuthenticatorSettingsComponent are all integrated. All you need to do is point it at your Auth0 tenant.
Configure Auth0
1. Get an Auth0 account and tenant
If you do not have an Auth0 account yet, you will need to sign up to automatically generate your first tenant
- Go to the Auth0 Sign Up page.
- Sign up using your social account (such as Google, GitHub, or Microsoft) or enter your email address and password.
- When prompted, choose your Account Type (for example, Personal) and click Next.
- Set up your initial Tenant Domain (this name must be unique, contains lowercase letters, numbers, and hyphens, and cannot be changed later).
- Select your preferred Region (this determines where your data is stored).
- Click Create to finish. You will be redirected straight to your new Auth0 Dashboard.
2. Set up a custom domain (required for passkeys)
Passkeys use your Auth0 domain as the WebAuthn Relying Party ID (RP ID). Auth0’s default domain (YOUR_TENANT.auth0.com) is shared across tenants, which causes passkey association to fail on Android. A custom domain gives your app an isolated RP ID tied exclusively to your domain.
To set up a custom domain, go to Branding → Custom Domains in the Auth0 Dashboard and follow the verification steps. Once verified, use your custom domain everywhere in this guide in place of your default Auth0 domain.
For a full walkthrough of custom domain setup, see the Auth0 Custom Domains documentation or our blog post on Setting Up Custom Domains with Auth0
3. Create a native application
In the Auth0 Dashboard, go to Applications → Create Application, choose Native, and give it a name.
4. Set callback URLs
In the application settings, set both Allowed Callback URLs and Allowed Logout URLs to:
app://YOUR_CUSTOM_DOMAIN/android/YOUR_ANDROID_PACKAGE_NAME/callback
Replace YOUR_CUSTOM_DOMAIN with your verified custom domain (for example, auth.yourdomain.com) and YOUR_ANDROID_PACKAGE_NAME with your Android package name.
5. Enable My Account API
My Account APIs power the account security components. Navigate to APIs in the sidebar and you will see an option to activate My Account API, click on it and it will be activated.
6. Allow Offline Access on My Account API
After activating the My Account API, navigate to APIs → My Account API → Settings and enable Allow Offline Access. This allows the offline_access scope to be issued for the My Account API audience, which is required for Multi-Resource Refresh Token (MRRT) to work. Without this, the SDK cannot exchange the refresh token for a My Account API access token.
7. Grant types
After creating the Native application, go to Applications → [Your App] → Settings → Advanced Settings → Grant Types and ensure the following are enabled:
- Authorization Code
- Refresh Token
- Passkeys
8. Multi-Resource Refresh Token (MRRT)
MRRT allows a single refresh token to be used to obtain access tokens for multiple different API audiences — without requiring the user to re-authenticate.
By default, a refresh token is bound to the audience it was issued for. With MRRT enabled, Auth0 accepts that same refresh token when your app requests a token for a different audience (such as the My Account API), and issues a new access token scoped to that resource.
Universal Native Components relies on MRRT internally. When a user navigates to a factor enrollment screen, the SDK calls DefaultTokenProvider.fetchApiCredentials() to fetch a just-in-time token scoped to the My Account API using the existing refresh token from login.
Without MRRT enabled on your tenant, this call returns an invalid_grant error and the account security screen fails to load.
To enable MRRT:
- Go to go to Applications → [Your App] → Settings → Multi-Resource Refresh Token → Edit Configuration
- Find and toggle ON the Auth0 My Account API
- Use the pencil icon button to make sure the following scopes are granted
read:me:authentication_methodsdelete:me:authentication_methodsupdate:me:authentication_methodsread:me:factorscreate:me:authentication_methods
- Save
9. Register your app for passkeys
Once your custom domain is verified, Android’s Credential Manager needs to confirm that your app is authorized to create passkeys for that domain. Auth0 handles this by serving a /.well-known/assetlinks.json file — but only after you register your app.
Go to Applications → Your App → Advanced Settings → Device Settings and add:
- Package name:
YOUR_ANDROID_PACKAGE_NAME - SHA-256 fingerprint: your signing certificate’s SHA-256 fingerprint
To get your debug fingerprint you run it in your Mac's Terminal app, or the built-in terminal in Android Studio:
keytool -list -v \ -keystore ~/.android/debug.keystore \ -alias androiddebugkey \ -storepass android \ -keypass android **|** grep SHA256
You can verify Auth0 is serving the file correctly by visiting: https://YOUR_CUSTOM_DOMAIN/.well-known/assetlinks.json
Your app’s package name and fingerprint should appear in the response.
10. Update your credentials in Android Studio
Open app/src/main/res/values/strings.xml and replace the placeholder values with your custom domain and client ID:
<resources> <string name="app_name">Auth0 Native Components</string> <string name="com_auth0_domain">YOUR_CUSTOM_DOMAIN</string> <string name="com_auth0_client_id">YOUR_CLIENT_ID</string> </resources>
That is the only code change required. Build and run the app.
What You Will See in the App
After signing in, tap Account Security on the home screen. You now have a fully functional native account security screen:
- All enrolled authentication methods are listed in one place.
- Tapping any factor walks through the enrollment flow — QR code scanning for TOTP, phone number entry for SMS, biometric prompt for passkeys.
- Enrolled methods can be removed directly from the list.
- If a flow requires step-up authentication, the SDK triggers it automatically.
- The back button navigates correctly through internal flows without popping back to your app’s nav stack.
How Auth0 Universal Native Components Work in the App
Here is how the integration works in the sample app, so you can apply the same pattern to your own project.
SDK initialization
Auth0UniversalComponents.initialize() is called once in MainApplication.onCreate(), after Auth0 and CredentialsManager are set up:
override fun onCreate() { super.onCreate() auth0 = Auth0.getInstance( clientId = getString(R.string.com_auth0_client_id), domain = getString(R.string.com_auth0_domain) ) auth0.networkingClient = DefaultClient(enableLogging = true) credentialsManager = CredentialsManager( AuthenticationAPIClient(auth0), SharedPreferencesStorage(this) ) Auth0UniversalComponents.initialize( context = this, account = auth0, tokenProvider = DefaultTokenProvider(credentialsManager), scheme = "app" ) }
DefaultTokenProvider bridges your existing CredentialsManager to the Universal Components SDK. The SDK requests scoped access tokens just-in-time as users navigate between account security flows. You don’t manage token scoping manually.
If you need custom credential handling (biometric-gated retrieval, hardware-backed key stores, or your own secure storage) implement the TokenProvider interface and pass it in place of DefaultTokenProvider:
class BiometricTokenProvider( private val credentialsManager: CredentialsManager ) : TokenProvider { override suspend fun fetchCredentials(): Credentials { /* ... */ } override suspend fun fetchApiCredentials(audience: String, scope: String?): APICredentials { /* ... */ } override suspend fun saveApiCredentials(audience: String, credentials: APICredentials) { /* ... */ } }
The component
The entire account security screen is a single composable in ui/SettingsScreen.kt:
import com.auth0.universalcomponents.presentation.ui.mfa.AuthenticatorSettingsComponent @Composable **fun** SettingsScreen() { AuthenticatorSettingsComponent() }
That one call can deliver the full account security lifecycle: enrollment, verification, and removal for all factor types, with internal navigation, step-up auth, and state restoration across configuration changes all handled by the library.
Customize the Theme with Auth0ThemeConfiguration
The component can adapt automatically to the system’s light/dark mode setting. To override this and force a specific appearance or match your app’s brand, pass an Auth0ThemeConfiguration:
@Composable fun SettingsScreen() { *// Force light theme regardless of system setting* AuthenticatorSettingsComponent( themeConfiguration = Auth0ThemeConfiguration(color = Auth0Color.light()) ) } @Composable fun SettingsScreen() { *// Force dark theme* AuthenticatorSettingsComponent( themeConfiguration = Auth0ThemeConfiguration(color = Auth0Color.dark()) ) }
Passing nothing, the default, follows the system setting automatically.
Improving Your Android App’s Security Management with Auth0 Universal Native Components
Connecting a production-ready account security experience to your Android app came down to three things:
- cloning the sample
- creating an Auth0 application in the dashboard
- adding your custom domain and client ID to strings.xml.
The AuthenticatorSettingsComponent does the rest — TOTP, push, SMS/email OTP, passkeys, recovery codes, step-up auth, dark mode, and back navigation, with no flow logic written on your end.
The full source code is available at GitHub.
Universal Native Components for Android is in Beta and actively expanding. Planned upcoming capabilities include embedded login flows (no browser redirect), connected account management, profile editing, and session management — all following the same “one composable” integration model.
To get started, check out the documentation. To share feedback during the Beta release, reach out to your Auth0 account team.


