Authentication - Swift SDK Documentation
Overview
Authentication in BosBase is stateless and token-based. A client is considered authenticated as long as it sends a valid Authorization: YOUR_AUTH_TOKEN header with requests.
Key Points:
- No sessions: BosBase APIs are fully stateless (tokens are not stored in the database)
- No logout endpoint: To “logout”, simply clear the token from your local state (
client.authStore.clear()) - Token generation: Auth tokens are generated through auth collection Web APIs or programmatically
- Admin users:
_superuserscollection works like regular auth collections but with full access (API rules are ignored) - OAuth2 limitation: OAuth2 is not supported for
_superuserscollection
📖 Reference: This guide mirrors the JavaScript SDK Authentication documentation but uses Swift syntax and examples.
Authentication Methods
BosBase supports multiple authentication methods that can be configured individually for each auth collection:
- Password Authentication - Email/username + password
- OTP Authentication - One-time password via email
- OAuth2 Authentication - Google, GitHub, Microsoft, etc.
- Multi-factor Authentication (MFA) - Requires 2 different auth methods
Authentication Store
The SDK maintains an authStore that automatically manages the authentication state:
import BosBase
let client = BosBaseClient(baseURL: URL(string: "http://localhost:8090")!)
// Check authentication status
print(client.authStore.isValid()) // true/false
print(client.authStore.token ?? "") // current auth token
print(client.authStore.record ?? [:]) // authenticated user record
// Listen for auth changes
client.authStore.onChange = { state in
print("Auth state changed - Token: \(state.token ?? "nil")")
}
// Clear authentication (logout)
client.authStore.clear()
Password Authentication
Authenticate using email/username and password. The identity field can be configured in the collection options (default is email).
Backend Endpoint: POST /api/collections/{collection}/auth-with-password
Basic Usage
import BosBase
let client = BosBaseClient(baseURL: URL(string: "http://localhost:8090")!)
// Authenticate with email and password
let authData = try await client
.collection("users")
.authWithPassword(identity: "test@example.com", password: "password123")
// Auth data is automatically stored in client.authStore
print(client.authStore.isValid()) // true
print(client.authStore.token ?? "") // JWT token
print(client.authStore.record?["id"] ?? "") // user record ID
Response Format
The authWithPassword method returns a RecordAuthResponse containing:
struct RecordAuthResponse: Decodable {
let token: String
let record: JSONRecord // Dictionary with user fields
}
Error Handling with MFA
do {
let auth = try await client
.collection("users")
.authWithPassword(identity: "test@example.com", password: "pass123")
} catch let error as ClientResponseError {
// Check for MFA requirement
if let mfaId = error.response?["mfaId"] as? String {
// Handle MFA flow (see Multi-factor Authentication section)
print("MFA required: \(mfaId)")
} else {
print("Authentication failed: \(error)")
}
} catch {
print("Unexpected error: \(error)")
}
OTP Authentication
One-time password authentication via email.
Backend Endpoints:
POST /api/collections/{collection}/request-otp- Request OTPPOST /api/collections/{collection}/auth-with-otp- Authenticate with OTP
Request OTP
// Request OTP to be sent via email
try await client
.collection("users")
.requestOTP(email: "test@example.com")
Authenticate with OTP
// Authenticate using the OTP ID and password received via email
let auth = try await client
.collection("users")
.authWithOTP(otpId: "OTP_ID_FROM_EMAIL", password: "OTP_PASSWORD")
OAuth2 Authentication
OAuth2 authentication supports multiple providers (Google, GitHub, Microsoft, etc.).
Backend Endpoints:
GET /api/collections/{collection}/auth-with-oauth2- Get OAuth2 URLPOST /api/collections/{collection}/auth-with-oauth2-code- Complete OAuth2 flow
OAuth2 with URL Callback
// Configure OAuth2 authentication
let config = OAuth2AuthConfig(
provider: "google",
scopes: ["email", "profile"],
urlCallback: { url in
// Open URL in browser or web view
// On iOS:
#if canImport(UIKit)
await UIApplication.shared.open(url)
#endif
}
)
// Start OAuth2 flow
let auth = try await client
.collection("users")
.authWithOAuth2(config: config)
OAuth2 with Code (Manual Flow)
// Step 1: Get OAuth2 URL (you handle the redirect manually)
// Step 2: After user authorizes, you receive a code
// Step 3: Complete authentication with the code
let auth = try await client
.collection("users")
.authWithOAuth2Code(
provider: "google",
code: "AUTHORIZATION_CODE",
codeVerifier: "CODE_VERIFIER", // From PKCE flow
redirectUrl: "https://yourapp.com/callback"
)
Refresh Authentication
Refresh the current authenticated record and auth token.
// Refresh the auth token
let auth = try await client
.collection("users")
.authRefresh()
Password Reset
Request Password Reset
// Send password reset email
try await client
.collection("users")
.requestPasswordReset(email: "test@example.com")
Confirm Password Reset
// Reset password using token from email
try await client
.collection("users")
.confirmPasswordReset(
resetToken: "TOKEN_FROM_EMAIL",
newPassword: "newpassword123",
newPasswordConfirm: "newpassword123"
)
Email Verification
Request Verification
// Send verification email
try await client
.collection("users")
.requestVerification(email: "test@example.com")
Confirm Verification
// Verify email using token from email
try await client
.collection("users")
.confirmVerification(verificationToken: "TOKEN_FROM_EMAIL")
Email Change
Request Email Change
// Request email change (requires authentication)
let auth = try await client
.collection("users")
.authWithPassword(identity: "old@example.com", password: "password")
try await client
.collection("users")
.requestEmailChange(newEmail: "new@example.com")
Confirm Email Change
// Confirm email change using token from email
try await client
.collection("users")
.confirmEmailChange(
emailChangeToken: "TOKEN_FROM_EMAIL",
userPassword: "currentpassword"
)
List Auth Methods
Get all available authentication methods for a collection:
let methods = try await client
.collection("users")
.listAuthMethods()
// methods contains available auth providers and methods
Impersonation
Impersonate another user (superuser only):
// Authenticate as superuser first
try await client
.collection("_superusers")
.authWithPassword(identity: "admin@example.com", password: "adminpass")
// Impersonate a user
let impersonatedAuth = try await client
.collection("users")
.impersonate(recordId: "USER_ID_TO_IMPERSONATE")
Multi-factor Authentication (MFA)
When MFA is enabled, the initial authentication may return an error with mfaId:
do {
let auth = try await client
.collection("users")
.authWithPassword(identity: "test@example.com", password: "pass123")
} catch let error as ClientResponseError {
if let mfaId = error.response?["mfaId"] as? String {
// First factor passed, now request OTP for second factor
try await client
.collection("users")
.requestOTP(email: "test@example.com")
// Then authenticate with OTP
let auth = try await client
.collection("users")
.authWithOTP(otpId: mfaId, password: "OTP_PASSWORD")
}
}
External Auth Providers
List External Auths
List all linked external auth providers for a record:
let externalAuths = try await client
.collection("users")
.listExternalAuths(recordId: "USER_ID")
Unlink External Auth
Unlink an external auth provider:
try await client
.collection("users")
.unlinkExternalAuth(recordId: "USER_ID", provider: "google")
Complete Example
import BosBase
// Initialize client
let client = try BosBaseClient(baseURLString: "http://localhost:8090")
// Authenticate user
do {
let auth = try await client
.collection("users")
.authWithPassword(identity: "user@example.com", password: "password123")
print("Authenticated: \(auth.record?["email"] ?? "")")
// Use authenticated client
let posts: ListResult<JSONRecord> = try await client
.collection("posts")
.getList()
print("Fetched \(posts.items.count) posts")
// Refresh token if needed
let refreshed = try await client
.collection("users")
.authRefresh()
// Logout
client.authStore.clear()
} catch let error as ClientResponseError {
print("Error: \(error.status) - \(error.response ?? [:])")
} catch {
print("Unexpected error: \(error)")
}
Best Practices
-
Store tokens securely: The
AuthStorekeeps tokens in memory by default. For persistence, implement a customAuthStorethat saves to Keychain (iOS) or UserDefaults. -
Handle token expiration: Implement automatic token refresh using the
authRefresh()method. -
Error handling: Always handle
ClientResponseErrorfor authentication failures. -
MFA support: Check for
mfaIdin error responses when MFA is enabled. -
OAuth2 redirects: Handle OAuth2 redirects properly in your app’s URL scheme or universal links.