This page provides practical examples of how to use the Solibo SDK in various scenarios.
A typical integration in an Android application involves configuring the SDK with platform-specific implementations for Fingerprinter and PushTokenProvider. See the advanced setup guide for detailed implementation examples. Using a Dependency Injection framework like Koin is recommended.
ApiModule.kt
import no.solibo.oss.sdk.SoliboSDK
import no.solibo.oss.sdk.auth.Auth
import org.koin.dsl.module
val apiModule = module {
single<SoliboSDK> {
SoliboSDK.createMobile(
userPoolId = "eu-west-1_...",
clientId = "...",
settings = get(), // Multiplatform Settings (e.g., SharedPreferences)
fingerprinter = get(), // Platform implementation
pushTokenProvider = get(), // e.g., Firebase Messaging
configure = {
onRefreshFailure = {
// Global refresh failure handler (e.g., redirect to login)
get<Auth>().clearSession()
}
// Optional: custom Ktor HttpClient configuration
httpClientConfig = {
install(Logging) {
level = LogLevel.INFO
}
}
}
)
}
// Expose sub-APIs for easier injection
single { get<SoliboSDK>().api }
single { get<SoliboSDK>().auth }
}
MainApplication.kt
import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
class MainApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@MainApplication)
modules(
platformModule, // Module containing Fingerprinter and PushTokenProvider
apiModule
)
}
}
}
For iOS applications using Swift, you can initialize the SDK through a shared KMP helper or directly. Below is an example of initializing the SDK in your App struct or AppDelegate.
iOSApp.swift
import SwiftUI
import Shared // Your shared KMP module
@main
struct SoliboHomeApp: App {
init() {
// Initialize Koin and the SDK with platform implementations
HelperKt.doInitKoin(
fingerprinter: iOSFingerprinter(),
pushTokenProvider: iOSPushTokenProvider(),
userPoolId: "eu-west-1_...",
clientId: "..."
)
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
The
HelperKtanddoInitKoinmethods are typically defined in your shared KMP module to bridge between Swift and Kotlin. For platform-specific implementations, see the advanced setup guide.
import no.solibo.oss.sdk.SoliboSDK
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val sdk = SoliboSDK.createMobile(
userPoolId = "eu-west-1_...",
clientId = "..."
)
// sdk.api.setInitialBaseUrl("https://home.solibo.no/api") // Optional, default is https://api.home.solibo.no
try {
val user = sdk.api.users.showUser()
println("Hello, ${user.name?.given}!")
} catch (e: Exception) {
println("Error fetching user: ${e.message}")
}
}
import no.solibo.oss.sdk.SoliboSDK
import no.solibo.oss.sdk.api.gen.models.UpdateBoardMemberCommand
suspend fun updateMember(sdk: SoliboSDK, boardId: String, memberId: String) {
sdk.api.board.updateBoardMember(
boardId = boardId,
memberId = memberId,
updateBoardMemberCommand = UpdateBoardMemberCommand(
role = "Chairman"
)
)
}
import no.solibo.oss.sdk.SoliboSDK
import no.solibo.oss.sdk.api.gen.models.ConversationMessagePayload
fun subscribeToMessages(sdk: SoliboSDK, conversationId: Long) {
sdk.eventBus.onEvent { event ->
val payload = event.payload
if (payload is ConversationMessagePayload && payload.conversationId == conversationId) {
println("New message: ${payload.message.text}")
}
}
}
import no.solibo.oss.sdk.SoliboSDK
import no.solibo.oss.sdk.api.gen.models.MfaType
suspend fun setupMfa(sdk: SoliboSDK) {
// 1. Associate TOTP
val association = sdk.auth.createTOTPMfa()
println("Scan this secret in your app: ${association.secretCode}")
// 2. Verify and enable
sdk.auth.verifyCreateTOTPMfa(code = "123456")
sdk.auth.createMfaPreference(MfaType.TOTP)
}
suspend fun handleMfaChallenge(sdk: SoliboSDK, code: String) {
// The SDK automatically handles challenge type tracking
val result = sdk.auth.answerMfaChallenge(code)
if (result.tokens != null) {
println("MFA successful!")
}
}
A typical integration in a React application involves configuring the SDK and wrapping the app with SoliboProvider. This should be placed inside your QueryClientProvider.
sdk.ts
import { CreateSdkArgs } from '@solibo/solibo-query';
export const sdkConfig: CreateSdkArgs = {
// domain: 'home.solibo.no', // Optional, default is api.home.solibo.no
cognitoUserPoolId: 'eu-west-1_...',
// Fingerprinter for Cognito Advanced Security (optional)
fingerprinter: {
print: (username: string) => {
// Access your platform's fingerprinting logic (e.g., AmazonCognitoAdvancedSecurityData)
return (window as any).AmazonCognitoAdvancedSecurityData?.getData(
username,
'user-pool-id',
'client-id'
);
},
},
// Global refresh failure handler (e.g., redirect to login)
refreshFailureHandler: {
onRefreshFailure: () => {
localStorage.removeItem('user_session');
window.location.href = '/auth/login';
},
},
};
App.tsx
import { StrictMode } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { SoliboProvider } from '@solibo/solibo-query';
import { sdkConfig } from './sdk';
const queryClient = new QueryClient();
export default function App() {
return (
<StrictMode>
<QueryClientProvider client={queryClient}>
<SoliboProvider config={sdkConfig}>
<YourRoutes />
</SoliboProvider>
</QueryClientProvider>
</StrictMode>
);
}
The @solibo/solibo-query package provides hooks that integrate with TanStack React Query.
import { useUser } from '@solibo/solibo-query'
function UserProfile() {
const { data: user, isPending, isError } = useUser()
if (isPending) return <div>Loading...</div>
if (isError || !user) return <div>Error: Could not load user</div>
return (
<div>
<h1>Welcome, {user.name.given}!</h1>
<p>Email: {user.email}</p>
</div>
)
}
import { useBoardMembers } from '@solibo/solibo-query'
function BoardMembersList({ boardId }: { boardId: bigint }) {
const { data: members, isLoading, error } = useBoardMembers(boardId)
if (isLoading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<ul>
{members?.map(member => (
<li key={member.id}>{member.name.given} {member.name.family}</li>
))}
</ul>
)
}
import { useCreateBoardMember } from '@solibo/solibo-query'
function AddMemberForm({ boardId }: { boardId: bigint }) {
const mutation = useCreateBoardMember()
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault()
// .mutateAsync for promise based mutations
mutation.mutate({
boardId,
createBoardMemberCommand: {
userId: '...',
role: 'Member'
}
})
}
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
<button type="submit" disabled={mutation.isPending}>
Add Member
</button>
{mutation.isError && <div>Error: {mutation.error.message}</div>}
</form>
)
}
The SDK provides hooks for managing Multi-factor Authentication. The useAnswerMfaChallenge hook automatically tracks the current challenge type, so you don’t need to pass it explicitly if you just initiated an auth flow.
import { useState } from 'react'
import {
useRemoveMfa,
useCreateMfaPreference,
useAnswerMfaChallenge,
useInitiateAuth
} from '@solibo/solibo-query'
import { MfaType, AuthChallengeType } from '@solibo/solibo-sdk'
function Login() {
const loginMutation = useInitiateAuth()
const verifyMutation = useAnswerMfaChallenge()
const [mfaChallenge, setMfaChallenge] = useState<AuthChallengeType | null>(null)
const [code, setCode] = useState('')
const handleLogin = async (e: React.FormEvent) => {
e.preventDefault()
const result = await loginMutation.mutateAsync({
creds: { login: '...', pwd: '...' },
fcmDevice: 'WEB'
})
if (result.challenge) {
setMfaChallenge(result.challenge.type)
}
}
const handleVerify = () => {
// No need to pass challengeType! The SDK tracks it.
verifyMutation.mutate({ code })
}
if (mfaChallenge) {
return (
<div>
<p>Enter your {mfaChallenge} code:</p>
<input value={code} onChange={e => setCode(e.target.value)} />
<button onClick={handleVerify}>Verify</button>
</div>
)
}
return (
<form onSubmit={handleLogin}>
{/* login fields */}
<button type="submit">Login</button>
</form>
)
}
function MfaSettings() {
const removeMfa = useRemoveMfa()
const setPreference = useCreateMfaPreference()
return (
<div>
<button onClick={() => removeMfa.mutate()}>Disable MFA</button>
<button onClick={() => setPreference.mutate({ mfaType: MfaType.SMS })}>
Use SMS MFA
</button>
</div>
)
}
function TotpSetup() {
const startTotp = useCreateTOTPMfa()
const verifyTotp = useVerifyCreateTOTPMfa()
const [secret, setSecret] = useState<string | null>(null)
const [code, setCode] = useState('')
const handleStart = async () => {
const res = await startTotp.mutateAsync()
setSecret(res.secretCode!)
}
const handleVerify = () => {
verifyTotp.mutate({ code, deviceName: 'My Phone' })
}
if (secret) {
return (
<div>
<p>Your TOTP Secret: {secret}</p>
<input value={code} onChange={e => setCode(e.target.value)} />
<button onClick={handleVerify}>Verify & Enable</button>
</div>
)
}
return <button onClick={handleStart}>Enroll in TOTP</button>
}
import { useEffect } from 'react'
import { useSoliboApi } from '@solibo/solibo-query'
function NotificationListener() {
const { eventBus } = useSoliboApi()
useEffect(() => {
// onEvent returns an unsubscribe function
const unsubscribe = eventBus.onEvent(({ payload }) => {
// Discriminator-based type check
if (payload.type === 'solibo.common.infrastructure.websockets.payload.ConversationMessagePayload') {
alert('New message received!')
}
})
return () => unsubscribe()
}, [eventBus])
return <div>Monitoring for live updates...</div>
}
For backend services, scripts, or other non-interactive clients, the SDK supports M2M authentication using OAuth 2.0 Client Credentials flow.
import no.solibo.oss.sdk.SoliboSDK
import kotlinx.coroutines.runBlocking
fun main() = runBlocking {
val sdk = SoliboSDK.createM2M(
clientId = "your-client-id",
clientSecret = "your-client-secret",
scopes = listOf("api.read", "api.write")
)
val user = sdk.api.users.showUser()
println("Service acting as: ${user.name?.given}")
}
import { SoliboSDKJsBuilder } from '@solibo/solibo-query';
async function run() {
const sdk = await SoliboSDKJsBuilder.createM2M(
'your-client-id',
'your-client-secret',
['api.read', 'api.write']
);
const user = await sdk.api.users.showUser();
console.log(`Service acting as: ${user.name?.given}`);
}