Installation
Add InsForge dependencies to your project
Maven Central
GitHub Packages
build.gradle.kts: repositories {
mavenLocal () // For local development
mavenCentral ()
}
dependencies {
implementation ( "dev.insforge:insforge-kotlin:0.1.6" )
}
First, create a GitHub Personal Access Token:
Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
Select permission: read:packages
Then configure your project using one of the following methods:
Option A: Environment Variables
settings.gradle.kts (or build.gradle.kts): repositories {
mavenCentral ()
maven {
url = uri ( "https://maven.pkg.github.com/InsForge/insforge-kotlin" )
credentials {
username = System. getenv ( "GITHUB_USER" ) ?: ""
password = System. getenv ( "GITHUB_TOKEN" ) ?: ""
}
}
}
build.gradle.kts: dependencies {
implementation ( "dev.insforge:insforge-kotlin:0.1.6" )
}
Set environment variables before building: export GITHUB_USER = "your-github-username"
export GITHUB_TOKEN = "your-personal-access-token"
Option B: gradle.properties (Local Development)
Add credentials to your global Gradle properties file: ~/.gradle/gradle.properties: gpr.user =your-github-username
gpr.token =ghp_xxxxxxxxxxxx
settings.gradle.kts: repositories {
mavenCentral ()
maven {
url = uri ( "https://maven.pkg.github.com/InsForge/insforge-kotlin" )
credentials {
username = providers. gradleProperty ( "gpr.user" ).orNull ?: ""
password = providers. gradleProperty ( "gpr.token" ).orNull ?: ""
}
}
}
build.gradle.kts: dependencies {
implementation ( "dev.insforge:insforge-kotlin:0.1.6" )
}
The ~/.gradle/gradle.properties file is stored outside your project, so credentials won’t be accidentally committed to version control.
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)
}
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 Level Description 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
Add Chrome Custom Tabs dependency to your build.gradle.kts:
dependencies {
implementation ( "androidx.browser:browser:1.9.0" )
}
Initialize InsForge SDK (With Chrome Custom Tabs and Session Storage)
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import dev.insforge.createInsforgeClient
import dev.insforge.ai.AI
import dev.insforge.auth.Auth
import dev.insforge.auth.BrowserLauncher
import dev.insforge.auth.ClientType
import dev.insforge.auth.SessionStorage
import dev.insforge.database.Database
import dev.insforge.functions.Functions
import dev.insforge.realtime.Realtime
import dev.insforge.storage.Storage
class InsforgeManager ( private val context: Context ) {
val client = createInsforgeClient (
baseUrl = "https://your-app.insforge.app" ,
anonKey = "your-anon-key"
) {
install (Auth) {
// 1. Chrome Custom Tabs - opens in-app, similar to iOS ASWebAuthenticationSession
browserLauncher = BrowserLauncher { url ->
val customTabsIntent = CustomTabsIntent. Builder ()
. setShowTitle ( true )
. build ()
// Handle non-Activity context safely
if (context is Activity) {
customTabsIntent. launchUrl (context, Uri. parse (url))
} else {
customTabsIntent.intent. addFlags (Intent.FLAG_ACTIVITY_NEW_TASK)
customTabsIntent. launchUrl (context, Uri. parse (url))
}
}
// 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 ()
}
}
// 4. set client type for mobile
clientType = ClientType.MOBILE
}
// 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)
}
}
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
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 (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 ()
}
Email Verification
For users who register with email, the InsForge backend provides three options:
No email verification - Users can sign in immediately after registration. SignUpResponse will have accessToken != null.
Link-based verification - Users must open their email and click the verification link before they can sign in.
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.
Method Description 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 = "user@example.com" ,
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.
Example
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 >
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")
}
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 ()
}
}
}
}
}
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
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 customTabsIntent = CustomTabsIntent. Builder ()
. setShowTitle ( true )
. build ()
// Handle non-Activity context safely
if (context is Activity) {
customTabsIntent. launchUrl (context, Uri. parse (url))
} else {
customTabsIntent.intent. addFlags (Intent.FLAG_ACTIVITY_NEW_TASK)
customTabsIntent. launchUrl (context, Uri. parse (url))
}
}
persistSession = true
sessionStorage = mySessionStorage
}
}
Configure App Link / Deep Link Callback
Same as signInWithDefaultPage().
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
}
}
Handle OAuth Callback
Same as signInWithDefaultPage().
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.
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 } " )
}
Password Reset
InsForge supports two password reset methods, configured in the backend:
Code method : User receives a 6-digit code via email, verifies it to get a reset token, then resets password
Link method : User receives a magic link via email containing the reset token, then resets password directly
sendPasswordReset()
Send a password reset email to the user. The email will contain either a 6-digit code or a magic link depending on the backend configuration.
Parameters
email (String) - User’s email address
Example
try {
client.auth. sendPasswordReset (email = "user@example.com" )
// Show message to check email
showMessage ( "If your email is registered, you will receive a password reset email." )
} catch (e: InsforgeHttpException ) {
Log. e ( "Auth" , "Failed to send reset email: ${ e.message } " )
}
exchangeResetPasswordToken()
Exchange a 6-digit reset code for a reset token. This method is only used with the code-based reset flow.
Parameters
email (String) - User’s email address
code (String) - 6-digit numeric code received via email
Returns
ResetTokenResponse
data class ResetTokenResponse (
/** Reset token to use with resetPassword() */
val token: String ,
/** Token expiration timestamp */
val expiresAt: String ?
)
Example
try {
val response = client.auth. exchangeResetPasswordToken (
email = "user@example.com" ,
code = "123456"
)
// Store the token and proceed to password reset screen
val resetToken = response.token
showPasswordResetScreen (token = resetToken)
} catch (e: InsforgeHttpException ) {
Log. e ( "Auth" , "Invalid or expired code: ${ e.message } " )
}
resetPassword()
Reset the user’s password using a reset token.
Parameters
newPassword (String) - New password meeting the configured requirements
otp (String) - Reset token (from exchangeResetPasswordToken() for code flow, or from magic link URL for link flow)
Example
try {
client.auth. resetPassword (
newPassword = "newSecurePassword123" ,
otp = resetToken
)
// Password reset successful
showMessage ( "Password reset successfully. You can now sign in." )
navigateToSignIn ()
} catch (e: InsforgeHttpException ) {
Log. e ( "Auth" , "Password reset failed: ${ e.message } " )
}
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 Code Description 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