Skip to main content

Installation

  1. Add InsForge dependencies to your project
build.gradle.kts:
repositories {
    mavenLocal() // For local development
    mavenCentral()
}

dependencies {
    implementation("dev.insforge:insforge-kotlin:0.1.5")
}
  1. Initialize InsForge SDK
import dev.insforge.createInsforgeClient
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.storage.Storage
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.ai.AI

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    install(Auth)
    install(Database)
    install(Storage)
    install(Functions)
    install(Realtime) {
        autoReconnect = true
        reconnectDelay = 5000
    }
    install(AI)
}
  1. Enable Logging (Optional)
For debugging, you can configure the SDK log level:
import dev.insforge.InsforgeLogLevel

val client = createInsforgeClient(
    baseUrl = "https://your-app.insforge.app",
    anonKey = "your-api-key"
) {
    // DEBUG: logs request method/URL and response status
    // VERBOSE: logs full headers and request/response bodies
    logLevel = InsforgeLogLevel.DEBUG

    install(Auth)
    install(Database)
    // ... other modules
}
Log LevelDescription
NONENo logging (default, recommended for production)
ERROROnly errors
WARNWarnings and errors
INFOInformational messages
DEBUGDebug info (request method, URL, response status)
VERBOSEFull details (headers, request/response bodies)
Use NONE or ERROR in production to avoid exposing sensitive data in logs.

Android Initialization

  1. Initialize InsForge SDK (With Local Storage and Browser Launcher for OAuth)
import android.content.Context
import android.content.Intent
import android.net.Uri
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.SessionStorage

class InsforgeManager(private val context: Context) {
    
    val client = createInsforgeClient(
        baseUrl = "https://your-app.insforge.app",
        anonKey = "your-anon-key"
    ) {
        install(Auth) {
            // 1. config BrowserLauncher (for OAuth)
            browserLauncher = BrowserLauncher { url ->
                val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                context.startActivity(intent)
            }
            
            // 2. enable session persistence
            persistSession = true
            
            // 3. config SessionStorage (use SharedPreferences)
            sessionStorage = object : SessionStorage {
                private val prefs = context.getSharedPreferences(
                    "insforge_auth", 
                    Context.MODE_PRIVATE
                )
                
                override suspend fun save(key: String, value: String) {
                    prefs.edit().putString(key, value).apply()
                }
                
                override suspend fun get(key: String): String? {
                    return prefs.getString(key, null)
                }
                
                override suspend fun remove(key: String) {
                    prefs.edit().remove(key).apply()
                }
            }
        }
        // Install Database module
        install(Database)

        // Install Realtime module for real-time subscriptions
        install(Realtime) {
            debug = true
        }
        // Install other modules
        install(Storage)
        install(Functions)
        install(AI)
    }
}
  1. Use Jetpack DataStore for Session Storage (Optional)
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

val Context.authDataStore: DataStore<Preferences> by preferencesDataStore(name = "insforge_auth")

class DataStoreSessionStorage(private val context: Context) : SessionStorage {
    
    override suspend fun save(key: String, value: String) {
        context.authDataStore.edit { prefs ->
            prefs[stringPreferencesKey(key)] = value
        }
    }
    
    override suspend fun get(key: String): String? {
        return context.authDataStore.data.map { prefs ->
            prefs[stringPreferencesKey(key)]
        }.first()
    }
    
    override suspend fun remove(key: String) {
        context.authDataStore.edit { prefs ->
            prefs.remove(stringPreferencesKey(key))
        }
    }
}

// Then use it in your InsForge client
install(Auth) {
    browserLauncher = ...
    persistSession = true
    sessionStorage = DataStoreSessionStorage(context)
}

signUp()

Create a new user account with email and password.

Parameters

  • email (String) - User’s email address
  • password (String) - User’s password
  • name (String?, optional) - User’s display name

Returns

SignUpResponse

SignUpResponse

data class SignUpResponse(
    /** User object (null when email verification is required) */
    val user: User? = null,
    /** Access token (null when email verification is required) */
    val accessToken: String? = null,
    /** Indicates if email verification is required before sign-in */
    val requireEmailVerification: Boolean = false,
    /** Redirect URL (if applicable) */
    val redirectTo: String? = null,
    /** CSRF token (if applicable) */
    val csrfToken: String? = null,
    /** Refresh token (null when email verification is required) */
    val refreshToken: String? = null
)

Example (Basic)

try {
    val result = client.auth.signUp(
        email = "[email protected]",
        password = "secure_password123",
        name = "John Doe"
    )

    if (result.requireEmailVerification) {
        // Email verification required - show verification screen
        showEmailVerificationScreen(email = "[email protected]")
    } else {
        result.user?.let { user ->
            // Sign up successful, no verification needed
            Log.d("Auth", "Welcome, ${user.profile?.name ?: user.email}!")
            navigateToDashboard()
        }
    }
} catch (e: InsforgeHttpException) {
    Log.e("Auth", "Sign up failed: ${e.message}")
}

Example (Complete Flow with Verification)

class AuthViewModel : ViewModel() {

    // Sign up and handle verification requirement
    suspend fun signUp(email: String, password: String, name: String?) {
        try {
            val result = client.auth.signUp(
                email = email,
                password = password,
                name = name
            )

            if (result.requireEmailVerification) {
                // Show verification code input screen
                // User will receive a 6-digit code via email
                _uiState.value = AuthUiState.RequiresVerification(email)
            } else if (result.accessToken != null) {
                // Registration complete, user is signed in
                _uiState.value = AuthUiState.Authenticated
            }
        } catch (e: InsforgeHttpException) {
            _uiState.value = AuthUiState.Error(e.message ?: "Sign up failed")
        }
    }

    // Verify email with 6-digit code
    suspend fun verifyEmail(email: String, code: String) {
        try {
            client.auth.verifyEmail(email = email, code = code)
            // Verification successful, user can now sign in
            _uiState.value = AuthUiState.VerificationSuccess
        } catch (e: InsforgeHttpException) {
            _uiState.value = AuthUiState.Error("Invalid verification code")
        }
    }

    // Resend verification code
    suspend fun resendVerificationCode(email: String) {
        try {
            client.auth.resendVerificationEmail(email = email)
            // Show success message
        } catch (e: InsforgeHttpException) {
            _uiState.value = AuthUiState.Error("Failed to resend code")
        }
    }
}

sealed class AuthUiState {
    object Initial : AuthUiState()
    object Authenticated : AuthUiState()
    object VerificationSuccess : AuthUiState()
    data class RequiresVerification(val email: String) : AuthUiState()
    data class Error(val message: String) : AuthUiState()
}

Example (Jetpack Compose with State Handling)

@Composable
fun SignUpScreen(
    onNavigateToDashboard: () -> Unit,
    onNavigateToSignIn: () -> Unit
) {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    var name by remember { mutableStateOf("") }
    var showVerification by remember { mutableStateOf(false) }
    var verificationCode by remember { mutableStateOf("") }
    var errorMessage by remember { mutableStateOf<String?>(null) }
    var isLoading by remember { mutableStateOf(false) }
    val scope = rememberCoroutineScope()

    if (showVerification) {
        // Verification code input screen
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "Verify Your Email",
                style = MaterialTheme.typography.headlineMedium
            )

            Spacer(modifier = Modifier.height(8.dp))

            Text(
                text = "Enter the 6-digit code sent to $email",
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )

            Spacer(modifier = Modifier.height(24.dp))

            OutlinedTextField(
                value = verificationCode,
                onValueChange = { verificationCode = it },
                label = { Text("Verification Code") },
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                modifier = Modifier.fillMaxWidth()
            )

            errorMessage?.let {
                Spacer(modifier = Modifier.height(8.dp))
                Text(it, color = MaterialTheme.colorScheme.error)
            }

            Spacer(modifier = Modifier.height(16.dp))

            Button(
                onClick = {
                    scope.launch {
                        isLoading = true
                        errorMessage = null
                        try {
                            client.auth.verifyEmail(email = email, code = verificationCode)
                            onNavigateToSignIn()
                        } catch (e: InsforgeHttpException) {
                            errorMessage = "Invalid verification code"
                        } finally {
                            isLoading = false
                        }
                    }
                },
                enabled = !isLoading && verificationCode.length == 6,
                modifier = Modifier.fillMaxWidth()
            ) {
                if (isLoading) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(24.dp),
                        color = MaterialTheme.colorScheme.onPrimary
                    )
                } else {
                    Text("Verify")
                }
            }

            Spacer(modifier = Modifier.height(8.dp))

            TextButton(
                onClick = {
                    scope.launch {
                        try {
                            client.auth.resendVerificationEmail(email = email)
                            // Show toast: "Verification code sent"
                        } catch (e: InsforgeHttpException) {
                            errorMessage = "Failed to resend code"
                        }
                    }
                }
            ) {
                Text("Resend Code")
            }
        }
    } else {
        // Sign up form
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = "Create Account",
                style = MaterialTheme.typography.headlineMedium
            )

            Spacer(modifier = Modifier.height(24.dp))

            OutlinedTextField(
                value = name,
                onValueChange = { name = it },
                label = { Text("Name (optional)") },
                modifier = Modifier.fillMaxWidth()
            )

            Spacer(modifier = Modifier.height(8.dp))

            OutlinedTextField(
                value = email,
                onValueChange = { email = it },
                label = { Text("Email") },
                keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email),
                modifier = Modifier.fillMaxWidth()
            )

            Spacer(modifier = Modifier.height(8.dp))

            OutlinedTextField(
                value = password,
                onValueChange = { password = it },
                label = { Text("Password") },
                visualTransformation = PasswordVisualTransformation(),
                modifier = Modifier.fillMaxWidth()
            )

            errorMessage?.let {
                Spacer(modifier = Modifier.height(8.dp))
                Text(it, color = MaterialTheme.colorScheme.error)
            }

            Spacer(modifier = Modifier.height(16.dp))

            Button(
                onClick = {
                    scope.launch {
                        isLoading = true
                        errorMessage = null
                        try {
                            val result = client.auth.signUp(
                                email = email,
                                password = password,
                                name = name.ifBlank { null }
                            )

                            if (result.requireEmailVerification) {
                                showVerification = true
                            } else if (result.accessToken != null) {
                                onNavigateToDashboard()
                            }
                        } catch (e: InsforgeHttpException) {
                            errorMessage = e.message
                        } finally {
                            isLoading = false
                        }
                    }
                },
                enabled = !isLoading && email.isNotBlank() && password.isNotBlank(),
                modifier = Modifier.fillMaxWidth()
            ) {
                if (isLoading) {
                    CircularProgressIndicator(
                        modifier = Modifier.size(24.dp),
                        color = MaterialTheme.colorScheme.onPrimary
                    )
                } else {
                    Text("Sign Up")
                }
            }
        }
    }
}

Email Verification

For users who register with email, the InsForge backend provides three options:
  1. No email verification - Users can sign in immediately after registration. SignUpResponse will have accessToken != null.
  2. Link-based verification - Users must open their email and click the verification link before they can sign in.
  3. Code-based verification - The InsForge backend sends a 6-digit verification code to the user’s email. The client app needs to display a verification screen where users can enter the code, then call verifyEmail(email, code) to complete verification. Only after this can users sign in with email + password.
When requireEmailVerification is true, the response will have:
  • accessToken = null
  • user = null
  • requireEmailVerification = true
This indicates that verification via option 2 or 3 is required before the user can sign in.
MethodDescription
verifyEmail(email, code)Verify email with 6-digit code
resendVerificationEmail(email)Resend verification code to email

signIn()

Sign in an existing user with email and password.

Example

try {
    val result = client.auth.signIn(
        email = "[email protected]",
        password = "secure_password123"
    )

    result.user?.let { user ->
        Log.d("Auth", "Welcome back, ${user.profile?.name ?: user.email}")
    }
} catch (e: InsforgeHttpException) {
    Log.e("Auth", "Sign in failed: ${e.message}")
}

Email Verification

If the sign in response is:
{"error":"FORBIDDEN","message":"Email verification required","statusCode":403,"nextActions":"Please verify your email address before logging in"}
This indicates that verification via option 2 or 3 (link or code, see signUp()) is required before the user can sign in.

signOut()

Sign out the current user.

Example

try {
    client.auth.signOut()
    Log.d("Auth", "User signed out")
} catch (e: InsforgeException) {
    Log.e("Auth", "Sign out failed: ${e.message}")
}

signInWithDefaultPage()

InsForge provides a hosted authentication page that supports:
  • OAuth Providers: Google, GitHub, Discord, LinkedIn, Facebook, Instagram, TikTok, Apple, X (Twitter), Spotify, Microsoft
  • Email + Password: Traditional email/password authentication
Start authentication flow with the hosted authentication page.
For direct OAuth provider authentication without the hosted page, use signInWithOAuthPage() instead.

Example

  1. Configure App Link / Deep Link Callback
Configure your callback Activity in AndroidManifest.xml:
  • Option A: Custom URL Scheme (for Development)
<activity
    android:name=".AuthCallbackActivity"
    android:launchMode="singleTask"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourapp" android:host="auth" android:path="/callback" />
    </intent-filter>
</activity>
  • Option B: App Links (for Production)
<activity
    android:name=".AuthCallbackActivity"
    android:launchMode="singleTask"
    android:exported="true">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="yourdomain.com" android:path="/auth/callback" />
    </intent-filter>
</activity>
  1. Initiate OAuth Login
// Call in your login screen
fun startLogin() {
    // Use Custom URL Scheme
    client.auth.signInWithDefaultPage("yourapp://auth/callback")

    // Or use App Links
    // client.auth.signInWithDefaultPage("https://yourdomain.com/auth/callback")
}
  1. Handle OAuth Callback
class AuthCallbackActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleIntent(intent)
    }
    
    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.let { handleIntent(it) }
    }
    
    private fun handleIntent(intent: Intent) {
        intent.data?.let { uri ->
            lifecycleScope.launch {
                try {
                    // Callback URL handled by SDK
                    val result = client.auth.handleAuthCallback(uri.toString())
                    
                    // Auth success, navigate to main screen
                    Toast.makeText(this@AuthCallbackActivity, 
                        "Success: ${result.email}", Toast.LENGTH_SHORT).show()
                    
                    startActivity(Intent(this@AuthCallbackActivity, MainActivity::class.java))
                    finish()
                    
                } catch (e: Exception) {
                    // Handle error
                    Toast.makeText(this@AuthCallbackActivity,
                        "Failed: ${e.message}", Toast.LENGTH_LONG).show()
                    finish()
                }
            }
        }
    }
}
Step 1: Create assetlinks.json Create a file named assetlinks.json:
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.yourcompany.yourapp",
      "sha256_cert_fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
      ]
    }
  }
]
Replace:
  • package_name: Your app’s package name
  • sha256_cert_fingerprints: Your app’s signing certificate SHA256 fingerprint
Get SHA256 Fingerprint:
# Debug keystore
keytool -list -v -keystore ~/.android/debug.keystore \
  -alias androiddebugkey -storepass android -keypass android \
  | grep "SHA256:"

# Release keystore
keytool -list -v -keystore your-release-key.keystore \
  -alias your-key-alias \
  | grep "SHA256:"
Step 2: Host the file Upload to:
https://yourdomain.com/.well-known/assetlinks.json
Verify hosting:
curl https://yourdomain.com/.well-known/assetlinks.json
# Should return your JSON configuration
Step 3: Configure AndroidManifest.xml
<activity android:name=".MainActivity">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data
            android:scheme="https"
            android:host="yourdomain.com"
            android:pathPrefix="/auth" />
    </intent-filter>
</activity>

Verification Tools

Android App Links:
adb shell am start -a android.intent.action.VIEW \
  -d "https://yourdomain.com/auth/callback?access_token=test"

Recommendation by Platform

PlatformDevelopmentProduction
macOS DesktopCustom URL SchemeCustom URL Scheme
iOS MobileCustom URL SchemeUniversal Links ⭐️
Android MobileCustom URI SchemeApp Links ⭐️

signInWithOAuthPage()

Sign in directly with a specific OAuth provider. Unlike signInWithDefaultPage() which shows a hosted page with all options, this method opens the OAuth provider’s authentication page directly in the system browser.

Supported Providers

enum class OAuthProvider(val value: String) {
    GOOGLE("google"),
    GITHUB("github"),
    DISCORD("discord"),
    LINKEDIN("linkedin"),
    FACEBOOK("facebook"),
    INSTAGRAM("instagram"),
    TIKTOK("tiktok"),
    APPLE("apple"),
    X("x"),
    SPOTIFY("spotify"),
    MICROSOFT("microsoft")
}

Parameters

  • provider (OAuthProvider) - The OAuth provider to authenticate with
  • redirectUri (String) - Callback URL where InsForge will redirect after authentication

Returns

String  // The OAuth authorization URL (also opens in browser automatically)

Example

  1. Configure BrowserLauncher
When creating the InsForge client, configure the browserLauncher to handle opening URLs:
val client = createInsforgeClient(baseURL, anonKey) {
    install(Auth) {
        browserLauncher = BrowserLauncher { url ->
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
            context.startActivity(intent)
        }
        persistSession = true
        sessionStorage = mySessionStorage
    }
}
  1. Configure App Link / Deep Link Callback
Configure your callback Activity in AndroidManifest.xml (same as signInWithDefaultPage):
  • Option A: Custom URL Scheme (for Development)
<activity
    android:name=".AuthCallbackActivity"
    android:launchMode="singleTask"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="yourapp" android:host="auth" android:path="/callback" />
    </intent-filter>
</activity>
  • Option B: App Links (for Production)
<activity
    android:name=".AuthCallbackActivity"
    android:launchMode="singleTask"
    android:exported="true">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="https" android:host="yourdomain.com" android:path="/auth/callback" />
    </intent-filter>
</activity>
  1. Initiate OAuth Login with Specific Provider
// Start OAuth flow with Google
fun startGoogleLogin() {
    lifecycleScope.launch {
        val authUrl = client.auth.signInWithOAuthPage(
            OAuthProvider.GOOGLE,
            "yourapp://auth/callback"
        )
        // Browser opens automatically via browserLauncher
    }
}

// Start OAuth flow with GitHub
fun startGitHubLogin() {
    lifecycleScope.launch {
        client.auth.signInWithOAuthPage(
            OAuthProvider.GITHUB,
            "yourapp://auth/callback"
        )
    }
}
  1. Handle OAuth Callback
class AuthCallbackActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        handleIntent(intent)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.let { handleIntent(it) }
    }

    private fun handleIntent(intent: Intent) {
        intent.data?.let { uri ->
            lifecycleScope.launch {
                try {
                    // Callback URL handled by SDK
                    val result = client.auth.handleAuthCallback(uri.toString())

                    // Auth success, navigate to main screen
                    Toast.makeText(this@AuthCallbackActivity,
                        "Success: ${result.email}", Toast.LENGTH_SHORT).show()

                    startActivity(Intent(this@AuthCallbackActivity, MainActivity::class.java))
                    finish()

                } catch (e: Exception) {
                    // Handle error
                    Toast.makeText(this@AuthCallbackActivity,
                        "Failed: ${e.message}", Toast.LENGTH_LONG).show()
                    finish()
                }
            }
        }
    }
}

Authentication Flow

1. App calls signInWithOAuthPage(provider, redirectUri)
2. SDK fetches the OAuth authorization URL from InsForge
3. SDK automatically opens the OAuth URL in system browser
4. User authenticates with the provider (Google, GitHub, etc.)
5. Provider redirects to InsForge, then InsForge redirects to your callback URL
6. Android intercepts callback URL (via Custom URL Scheme or App Links)
7. App calls handleAuthCallback(url)
8. SDK creates session, updates auth state, and persists token

Jetpack Compose Example

@Composable
fun OAuthLoginScreen() {
    val scope = rememberCoroutineScope()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = "Sign in with",
            style = MaterialTheme.typography.headlineSmall
        )

        Spacer(modifier = Modifier.height(24.dp))

        // Google Sign In
        Button(
            onClick = {
                scope.launch {
                    client.auth.signInWithOAuthPage(
                        OAuthProvider.GOOGLE,
                        "yourapp://auth/callback"
                    )
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Continue with Google")
        }

        Spacer(modifier = Modifier.height(8.dp))

        // GitHub Sign In
        Button(
            onClick = {
                scope.launch {
                    client.auth.signInWithOAuthPage(
                        OAuthProvider.GITHUB,
                        "yourapp://auth/callback"
                    )
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Continue with GitHub")
        }

        Spacer(modifier = Modifier.height(8.dp))

        // Discord Sign In
        Button(
            onClick = {
                scope.launch {
                    client.auth.signInWithOAuthPage(
                        OAuthProvider.DISCORD,
                        "yourapp://auth/callback"
                    )
                }
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Continue with Discord")
        }
    }
}
The browserLauncher must be configured when installing the Auth module. If not configured, signInWithOAuthPage() will throw an IllegalStateException.

getCurrentUser()

Fetch the current authenticated user from the server. This is a suspend function that makes a network request.

Returns

CurrentUserResponse  // Contains user data from server

Example

try {
    val response = client.auth.getCurrentUser()
    Log.d("Auth", "Email: ${response.email}")
    Log.d("Auth", "Name: ${response.profile?.name ?: "N/A"}")
} catch (e: InsforgeHttpException) {
    Log.e("Auth", "Failed to get user: ${e.message}")
}
This method makes a network request to fetch user data. For accessing locally cached user state, use currentUser StateFlow instead.

currentSession

A StateFlow property that holds the current session from local storage.

Type

val currentSession: StateFlow<Session?>

Example

// Access current session value
client.auth.currentSession.value?.let { session ->
    Log.d("Auth", "Session active for user: ${session.user.email}")
}

// Observe session changes
lifecycleScope.launch {
    client.auth.currentSession.collect { session ->
        if (session != null) {
            Log.d("Auth", "Session active: ${session.user.email}")
        } else {
            Log.d("Auth", "No active session")
        }
    }
}

updateProfile()

Update current user’s profile.

Parameters

  • profile (Map\<String, Any\>) - Profile fields to update

Returns

ProfileResponse  // Updated profile data

Example

try {
    val result = client.auth.updateProfile(
        mapOf(
            "name" to "JohnDev",
            "bio" to "Android Developer",
            "avatar_url" to "https://example.com/avatar.jpg"
        )
    )
    Log.d("Auth", "Profile updated: ${result.name}")
} catch (e: InsforgeHttpException) {
    Log.e("Auth", "Update failed: ${e.message}")
}

currentUser

A StateFlow property that holds the current authenticated user state.

Type

val currentUser: StateFlow<User?>

Example

// Observe auth state as Flow
lifecycleScope.launch {
    client.auth.currentUser.collect { user ->
        if (user != null) {
            Log.d("Auth", "User signed in: ${user.email}")
        } else {
            Log.d("Auth", "User signed out")
        }
    }
}

// Access current user value directly
val user = client.auth.currentUser.value

Jetpack Compose Integration

@Composable
fun AuthScreen() {
    val currentUser by client.auth.currentUser.collectAsState()

    when {
        currentUser != null -> DashboardScreen(user = currentUser!!)
        else -> LoginScreen()
    }
}

@Composable
fun LoginScreen() {
    var email by remember { mutableStateOf("") }
    var password by remember { mutableStateOf("") }
    var isLoading by remember { mutableStateOf(false) }
    var error by remember { mutableStateOf<String?>(null) }
    val scope = rememberCoroutineScope()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        OutlinedTextField(
            value = email,
            onValueChange = { email = it },
            label = { Text("Email") },
            modifier = Modifier.fillMaxWidth()
        )

        Spacer(modifier = Modifier.height(8.dp))

        OutlinedTextField(
            value = password,
            onValueChange = { password = it },
            label = { Text("Password") },
            visualTransformation = PasswordVisualTransformation(),
            modifier = Modifier.fillMaxWidth()
        )

        error?.let {
            Spacer(modifier = Modifier.height(8.dp))
            Text(it, color = MaterialTheme.colorScheme.error)
        }

        Spacer(modifier = Modifier.height(16.dp))

        Button(
            onClick = {
                scope.launch {
                    isLoading = true
                    error = null
                    try {
                        client.auth.signIn(email, password)
                    } catch (e: InsforgeHttpException) {
                        error = e.message
                    } finally {
                        isLoading = false
                    }
                }
            },
            enabled = !isLoading,
            modifier = Modifier.fillMaxWidth()
        ) {
            if (isLoading) {
                CircularProgressIndicator(
                    modifier = Modifier.size(24.dp),
                    color = MaterialTheme.colorScheme.onPrimary
                )
            } else {
                Text("Sign In")
            }
        }

        Spacer(modifier = Modifier.height(16.dp))

        // OAuth button
        Button(
            onClick = {
                client.auth.signInWithDefaultPage("yourapp://auth/callback")
            },
            modifier = Modifier.fillMaxWidth()
        ) {
            Text("Sign In with OAuth")
        }
    }
}

Error Handling

import dev.insforge.exceptions.InsforgeHttpException
import dev.insforge.exceptions.InsforgeException

try {
    val result = client.auth.signIn(email, password)
} catch (e: InsforgeHttpException) {
    // HTTP errors from API with error codes
    when (e.error) {
        "INVALID_CREDENTIALS" -> showError("Invalid email or password")
        "USER_NOT_FOUND" -> showError("User not found")
        "EMAIL_NOT_VERIFIED" -> showError("Please verify your email")
        "INVALID_EMAIL" -> showError("Invalid email format")
        "WEAK_PASSWORD" -> showError("Password is too weak")
        else -> showError("Error: ${e.message}")
    }
} catch (e: InsforgeException) {
    // Other SDK errors (network, parsing, etc.)
    showError("Error: ${e.message}")
}

Common Error Codes

Error CodeDescription
INVALID_CREDENTIALSEmail or password is incorrect
USER_NOT_FOUNDNo user with this email exists
EMAIL_NOT_VERIFIEDEmail verification required
INVALID_EMAILEmail format is invalid
WEAK_PASSWORDPassword doesn’t meet requirements
USER_ALREADY_EXISTSEmail is already registered
SESSION_EXPIREDSession has expired, re-login required