iOS SDK
Abrova Auth for iOS and macOS (Abrova Auth, Swift). Native Sign in with Apple in one call, plus anonymous, Google, e-mail and password, magic link and Telegram sign-in. Session in the Keychain; token provider for every Abrova SDK.
Uses the public API key only. Safe to ship in an app
signInWithAppleNative() runs the sheet, nonce included
Refresh token on this device only; access token in memory
tokenProvider and strictTokenProvider for the other Abrova SDKs
async/await API
linkApple, linkGoogle, linkPassword keep the user id
This page describes AbrovaAuth 0.1.1.
iOS 13 or later, or macOS 12 or later. Swift 5.7 or later.
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#
Install with Swift Package Manager. In Xcode: File → Add Packages → enter the repository URL:
https://github.com/Abrova-co/abrova-auth-ios-sdk
Or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/Abrova-co/abrova-auth-ios-sdk.git", from: "0.1.1")
]Then add AbrovaAuth to your target's dependencies.
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 at app start, before any other call, with your public API key (ab_live_...). A session saved by an earlier launch is restored.
import AbrovaAuth
@main
struct MyApp: App {
init() {
AbrovaAuth.shared.configure(apiKey: "ab_live_your_api_key")
}
var body: some Scene { WindowGroup { ContentView() } }
}| Parameter | Default | Description |
|---|---|---|
| apiKey | - | Your public API key (ab_live_...). Never the server secret. |
| keychainAccessGroup | nil | Share the session with your app extensions. |
The Keychain survives an uninstall, so the SDK forgets a session left by a previous install: a fresh install starts signed out.
Sign in#
let auth = AbrovaAuth.shared
let methods = try await auth.getMethods()
if auth.currentUserId == nil && methods.isEnabled("anonymous") {
try await auth.signInAnonymously()
}
print(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.shared.setUserId(auth.currentUserId).// Abrova Sync sync.userTokens.provider = auth.tokenProvider // Abrova Push AbrovaPushConfig(apiKey: key, tokenProvider: auth.tokenProvider) // Abrova Chat: create the client after sign-in AbrovaChatConfig(apiKey: key, userId: auth.currentUserId!, tokenProvider: auth.strictTokenProvider)
tokenProvider returns nil when nobody is signed in; strictTokenProvider throws. Both 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.refreshUserToken()). 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 Apple
Add the Sign in with Apple capability to your app and your bundle ID under Abrova Auth › Sign-in methods › Apple. Then:
try await AbrovaAuth.shared.signInWithAppleNative()
The SDK runs Apple's sheet with a nonce and passes the authorization code (so account deletion can revoke the Apple sign-in) and the name Apple shares on the first sign-in. It throws .canceled when the user closes the sheet. If you run the Apple request yourself:
let rawNonce = AbrovaNonce.generate()
request.nonce = AbrovaNonce.sha256(rawNonce) // to Apple
// ... when Apple answers:
try await auth.signInWithApple(idToken: idToken, rawNonce: rawNonce,
authorizationCode: code, fullName: name)Sign in with Google
Request the Google ID token with AbrovaNonce.sha256(rawNonce) as its nonce, then:
try await auth.signInWithGoogle(idToken: idToken, rawNonce: rawNonce)
Google sign-in needs a connection to Google, which some users in Iran don’t have. 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.
E-mail and password
let result = try await auth.signUp(email: email, password: password,
redirectURL: "https://example.com/auth/verify")
// result.signedIn is false when the project requires a verified e-mail first.
try await auth.signIn(email: email, password: password)
try await auth.sendPasswordReset(email: email, redirectURL: "https://example.com/auth/reset")
try await auth.resetPassword(token: token, newPassword: newPassword) // ends every session
try await auth.verifyEmail(token: token)
try await auth.sendEmailVerification(redirectURL: "https://example.com/auth/verify")AbrovaAuth.token(in: url) reads the token from the link the app was opened with.
Magic link
try await auth.sendMagicLink(email: email, redirectURL: "myapp://auth/magic") // When the app is opened with the link: try await auth.signInWithMagicLink(url: openedURL)
Telegram
let code = try await auth.requestTelegramCode(phone: "+989121234567")
if !code.sent, let url = code.botURL { await UIApplication.shared.open(url) }
try await auth.verifyTelegramCode(phone: "+989121234567", code: "123456")Upgrade an anonymous user#
Linking keeps the user id, so everything they created stays theirs:
do {
try await auth.signInWithAppleNative(link: true)
// or linkGoogle(idToken:rawNonce:), linkApple(...), linkPassword(email:password:)
} catch let error as AbrovaAuthError where error.code == .providerAlreadyLinked {
// This Apple ID already belongs to another user.
}The current user#
auth.currentUserId // nil when signed out
auth.isSignedIn
let user = try await auth.me() // AbrovaUser: id, isAnonymous, emailVerified, createdAt, providers
auth.currentUser // the profile last loaded by me()
let listener = auth.addAuthStateListener { userId in
// On the main queue: now, then on every sign-in, sign-out and session end
}
listener.remove()
let token = try await auth.getAccessToken() // if you call your own API with itSign out and delete the account#
await auth.signOut() // this device; never fails try await auth.signOutEverywhere() // every device try await auth.deleteAccount() // required by the App Store for apps with sign-in
See Security & data for what deletion removes.
Handle errors#
Every error is an AbrovaAuthError. Branch on code:
do {
try await auth.signIn(email: email, password: password)
} catch let error as AbrovaAuthError {
switch error.code {
case .invalidCredentials: showError("Wrong e-mail or password")
case .rateLimited: showError("Try again in \(error.retryAfterSec ?? 60) s")
case .network: showError("No connection")
default: showError("Sign-in failed")
}
}message is an English hint for developers. Besides the server's codes the SDK uses .network, .canceled (the user closed the Apple sheet), .notSignedIn and .sdkNotConfigured (call configure first). A network error never signs the user out. All codes: Error codes.
Troubleshooting#
SDK_NOT_CONFIGURED
Call AbrovaAuth.shared.configure(apiKey:) at app start, before any other call.
Sign in with Apple fails with INVALID_ID_TOKEN
Add your app's bundle ID under Abrova Auth › Sign-in methods › Apple, and the Sign in with Apple capability to the target.
METHOD_DISABLED or PRODUCT_DISABLED
Turn on Abrova Auth under Settings › Products and the method under Abrova Auth › Sign-in methods.