Android SDK
Abrova Auth for Android (ir.abrova:abrova-auth, Kotlin). Anonymous, Google (Credential Manager), Apple, e-mail and password, magic link and Telegram sign-in with coroutines; refresh token encrypted with an Android Keystore key; token providers for every Abrova SDK.
Uses the public API key only. Safe to ship in an app
Every call is a suspend function
Refresh token encrypted with a key that never leaves the Android Keystore
Suspend, strict and blocking providers for the other Abrova SDKs
authState for Compose and coroutines, or a classic listener
linkApple, linkGoogle, linkPassword keep the user id
This page describes ir.abrova:abrova-auth 0.1.1.
Android API 23 (Android 6.0) or higher. Kotlin coroutines.
Using an AI assistant? Give it this page with Copy page for AI, or all the docs as plain text from /docs/llms.txt. See AI assistants.
Installation#
The SDK comes from Abrova Center. First add the Abrova Maven repository in settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://center.abrova.ir/api/packages/abrova/maven") }
}
}Then add the dependency in your app's build.gradle.kts:
dependencies {
implementation("ir.abrova:abrova-auth:0.1.1")
}In Abrova Console, turn on Abrova Auth for your project (Settings › Products) and the sign-in methods you want (Abrova Auth › Sign-in methods).
Initialization#
Configure the SDK once, in Application.onCreate, with your public API key (ab_live_...). A session saved by an earlier launch is restored.
import ir.abrova.auth.AbrovaAuth
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
AbrovaAuth.shared.configure(this, "ab_live_your_api_key")
}
}All sign-in calls are suspend functions: call them from a coroutine (for example lifecycleScope.launch { ... }).
Sign in#
val auth = AbrovaAuth.shared
val methods = auth.getMethods()
if (auth.currentUserId == null && methods.isEnabled("anonymous")) {
auth.signInAnonymously()
}
Log.d("Auth", auth.currentUserId!!) // ru_...Show only the buttons for methods where methods.isEnabled(...) is true: anonymous, apple, google, password, magicLink, telegram.
Use the user with other Abrova SDKs#
AbrovaTrace.setUserId(auth.currentUserId).// Abrova Sync: its provider is called off the main thread AbrovaSyncConfig.builder(apiKey).userTokenProvider(auth.blockingTokenProvider).build() // Abrova Push AbrovaPushConfig(apiKey = apiKey, tokenProvider = auth.tokenProvider) // Abrova Chat: create the client after sign-in AbrovaChatConfig(apiKey = apiKey, userId = auth.currentUserId!!, tokenProvider = auth.strictTokenProvider)
| Provider | Type | When nobody is signed in |
|---|---|---|
| tokenProvider | suspend () -> String? | Returns null |
| strictTokenProvider | suspend () -> String | Throws NOT_SIGNED_IN |
| blockingTokenProvider | () -> String? | Returns null. Blocks the calling thread: never call it on the main thread |
All return a token that is renewed before it expires. After a sign-in or sign-out, call the other SDK's refresh method (for example sync.userTokens.refresh()). Abrova A/B Testing and more: Use with other Abrova products.
Sign-in methods#
Each method has its own page with the Console setup: Anonymous, Apple, Google, E-mail & password, Magic link, Telegram.
Sign in with Google
Use Credential Manager with your web client ID and a hashed nonce. Add androidx.credentials:credentials, androidx.credentials:credentials-play-services-auth and com.google.android.libraries.identity.googleid:googleid to your app:
val rawNonce = AbrovaNonce.generate()
val option = GetGoogleIdOption.Builder()
.setServerClientId(WEB_CLIENT_ID)
.setFilterByAuthorizedAccounts(false)
.setNonce(AbrovaNonce.sha256(rawNonce))
.build()
val response = CredentialManager.create(context)
.getCredential(context, GetCredentialRequest.Builder().addCredentialOption(option).build())
val idToken = GoogleIdTokenCredential.createFrom(response.credential.data).idToken
auth.signInWithGoogle(idToken, rawNonce)Add the web client ID under Abrova Auth › Sign-in methods › Google.
Google sign-in needs a connection to Google, and on Android also Google Play services; some users in Iran have neither. Anonymous, e-mail and password, magic link and Telegram code work without Google, so offer at least one of them as well. See Sign in with Google.
Sign in with Apple
Run Apple's web sign-in with your Services ID and AbrovaNonce.sha256(rawNonce) as the nonce, then pass the id_token and code Apple returns:
auth.signInWithApple(idToken, rawNonce, authorizationCode = code)
E-mail and password
val result = auth.signUp(email, password, redirectUrl = "https://example.com/auth/verify") // result.signedIn is false when the project requires a verified e-mail first. auth.signIn(email, password) auth.sendPasswordReset(email, redirectUrl = "https://example.com/auth/reset") auth.resetPassword(token, newPassword) // ends every session; does not sign in auth.verifyEmail(token) auth.sendEmailVerification(redirectUrl = "https://example.com/auth/verify")
Magic link
auth.sendMagicLink(email, redirectUrl = "myapp://auth/magic")
// In the activity the link opens:
AbrovaAuth.tokenFromLink(intent.dataString ?: "")?.let { auth.signInWithMagicLink(it) }Telegram
val code = auth.requestTelegramCode("+989121234567")
if (!code.sent) startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(code.botUrl)))
auth.verifyTelegramCode("+989121234567", "123456")Upgrade an anonymous user#
Linking keeps the user id, so everything they created stays theirs:
try {
auth.linkGoogle(idToken, rawNonce) // or linkApple(...), linkPassword(email, password)
} catch (e: AbrovaAuthException) {
if (e.code == AbrovaAuthErrorCode.PROVIDER_ALREADY_LINKED) {
// This Google account already belongs to another user.
}
}The current user#
auth.currentUserId // null when signed out
auth.isSignedIn
val user = auth.me() // AbrovaUser: id, isAnonymous, emailVerified, createdAt, providers
auth.currentUser // the profile last loaded by me()
// StateFlow of the user id
lifecycleScope.launch { auth.authState.collect { userId -> /* sign-in, sign-out, session end */ } }
// Or a listener
val registration = auth.addAuthStateListener { userId -> }
registration.remove()
val token = auth.getAccessToken() // if you call your own API with itSign out and delete the account#
auth.signOut() // this device; never fails auth.signOutEverywhere() // every device auth.deleteAccount() // deletes the user, then signs out
See Security & data for what deletion removes.
Handle errors#
Every error is an AbrovaAuthException. Branch on code:
try {
auth.signIn(email, password)
} catch (e: AbrovaAuthException) {
when (e.code) {
AbrovaAuthErrorCode.INVALID_CREDENTIALS -> showError("Wrong e-mail or password")
AbrovaAuthErrorCode.RATE_LIMITED -> showError("Try again in ${e.retryAfterSec ?: 60} s")
AbrovaAuthErrorCode.NETWORK_ERROR -> showError("No connection")
else -> showError("Sign-in failed")
}
}message is an English hint for developers. Besides the server's codes the SDK uses NETWORK_ERROR, NOT_SIGNED_IN and SDK_NOT_CONFIGURED (call configure first). A network error never signs the user out. All codes: Error codes.
Troubleshooting#
SDK_NOT_CONFIGURED
Call AbrovaAuth.shared.configure(context, apiKey) in Application.onCreate.
Google sign-in fails with INVALID_ID_TOKEN
Credential Manager issues the ID token to the web client ID you pass as serverClientId. Add that web client ID in the Console, and pass AbrovaNonce.sha256(rawNonce) as the nonce.
The app freezes when Sync asks for a token
blockingTokenProvider blocks its thread. Give it only to SDKs that call it off the main thread, and never call it yourself on the main thread.