Authentication
iOS Authentication
This page explains OAuth sign-in, JWT login, session handling, and biometric features for the iOS SDK.
Login provider
Choose provider with signIn(provider:):
// Show provider selection sheet (Google + Apple)
let auth = try await sdk.signIn(provider: .all)
// Direct Google login
let auth = try await sdk.signIn(provider: .google)
// Direct Apple login
let auth = try await sdk.signIn(provider: .apple)One-step login + wallet creation
signInWithCreate combines login and wallet creation into a single call. If the user already has a wallet, it returns immediately without creating a new one.
let auth = try await sdk.signInWithCreate(provider: .all)
// auth.walletAddress is guaranteed to be non-nil on success- If
needsMigrationistrueafter login,createWallet()is called automatically to trigger the migration flow. - If a wallet already exists, no additional creation is performed.
JWT login (token injection)
If your app already has JWT tokens from an external auth system, inject them directly without the OAuth browser flow:
let auth = try await sdk.signInWithJWT(
accessToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
refreshToken: "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4..."
)refreshTokenis optional — ifnil, the SDK cannot auto-refresh when the access token expires.- Throws
CROSSxError.tokenExpiredif the provided JWT is already expired.
OAuth callback handling (required)
After ASWebAuthenticationSession login, forward the callback URL into the SDK:
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
return sdk.handleURL(url)
}If callback handling is missing, signIn() cannot complete.
Session lifecycle
initialize()
initialize()Try restoring stored session on app start:
let restored = try await sdk.initialize() // AuthResult? (nil if no session)isLoggedIn()
isLoggedIn()Fast local check only (no refresh call):
let loggedIn = sdk.isLoggedIn()ensureLoggedIn()
ensureLoggedIn()Ensures a usable session now (includes restore/refresh attempt):
let ok = await sdk.ensureLoggedIn()refreshToken()
refreshToken()Explicitly refresh the access token:
let newAccessToken = try await sdk.refreshToken()- Returns the current token if still valid, otherwise refreshes via the refresh token.
- Throws
CROSSxError.sessionExpiredif the refresh token is also invalid.
Sign-in / Sign-out
// Open OAuth (ASWebAuthenticationSession)
let auth = try await sdk.signIn()
// Sign in and create or migrate wallet in one step
let authWithWallet = try await sdk.signInWithCreate()
// Clear local session
try await sdk.signOut()After sign-in
After signIn(), continue with wallet APIs (for example, create/resolve address) in the same authenticated session.
let ok = await sdk.ensureLoggedIn()
if !ok { return }
// Create wallet if needed (idempotent on backend side).
let created = try await sdk.createWallet()
// Check wallet existence/state via address list.
let addresses = try await sdk.getAddresses()Use this pattern when your app flow is "login -> immediately perform wallet action".
Wallet check
Check the wallet status without creating or modifying anything:
let status = try await sdk.checkWallet()
switch status {
case .exists:
// Wallet exists — proceed with wallet operations
break
case .migrationRequired:
// Legacy backup found — call createWallet() to trigger migration
let wallet = try await sdk.createWallet()
case .notFound:
// No wallet — call createWallet() to create a new one
let wallet = try await sdk.createWallet()
}Password verification
Verify a wallet PIN against the server without caching it:
let isValid = try await sdk.verifyPassword("123456")Cloud backup status
Both initialize() and signIn() automatically check for a legacy native app wallet backup and include the result in AuthResult.cloudBackupStatus.
let auth = try await sdk.signIn()
if let backup = auth.cloudBackupStatus, backup.hasBackup {
// A legacy backup exists — the user can recover it via createWallet()
let recovered = try await sdk.createWallet()
}cloudBackupStatusisnilif the backup check fails (does not affect login success).hasBackup: truemeans a previous PIN-based backup is available for migration.
Migration status
signIn() also checks the wallet status and sets AuthResult.needsMigration:
let auth = try await sdk.signIn()
if auth.needsMigration == true {
// Legacy wallet exists — createWallet() will trigger migration UI
let wallet = try await sdk.createWallet()
}User info
Retrieve comprehensive user information including provider details and wallet addresses:
let userInfo = try await sdk.getUserInfo()
print(userInfo.data.provider) // "google" or "apple"
print(userInfo.data.email) // user email
print(userInfo.data.sub) // JWT subject (user ID)
print(userInfo.data.providerSub) // OAuth provider's original sub (Google/Apple)
print(userInfo.data.accessToken) // current access token
print(userInfo.loginType) // "google" or "apple"
print(userInfo.addresses) // [WalletAddressInfo]Biometric authentication
The SDK supports Face ID / Touch ID to protect the wallet PIN stored in Keychain.
// Check if the device supports biometric auth
let canUse = sdk.canUseBiometric()
// Check if biometric protection is currently enabled
let isEnabled = sdk.isBiometricEnabled()
// Enable or disable biometric protection
try await sdk.setBiometricEnabled(true)
try await sdk.setBiometricEnabled(false)setBiometricEnabled(true)saves the PIN to Keychain with Face ID / Touch ID protection.- If the device has no enrolled biometrics,
canUseBiometric()returnsfalse. - If no PIN is stored,
setBiometricEnabled(_:)shows an Enter PIN modal first.
Session expiration handling
When the session expires (both access and refresh tokens are invalid), the SDK throws CROSSxError.sessionExpired. Handle this to redirect the user to login:
do {
let data = try await sdk.getBalance(address: addr, chainId: chainId)
} catch CROSSxError.sessionExpired {
// Redirect to login flow
let auth = try await sdk.signIn()
}Recommended app flow
- App launch: call
initialize() - Before protected action: call
ensureLoggedIn() - User login action: call
signIn()orsignInWithCreate() - User logout action: call
signOut()
Updated 1 day ago