> ## Documentation Index
> Fetch the complete documentation index at: https://docs.insforge.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Referencia de SDK de autenticación

> Autenticación de usuarios y gestión de perfiles con el SDK de Kotlin de InsForge

## Instalación

1. Add InsForge dependencies to your project

<Tabs>
  <Tab title="Maven Central">
    build.gradle.kts:

    ```kotlin theme={null}
    repositories {
        mavenLocal() // For local development
        mavenCentral()
    }

    dependencies {
        implementation("dev.insforge:insforge-kotlin:0.1.6")
    }
    ```
  </Tab>

  <Tab title="GitHub Packages">
    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:

    <AccordionGroup>
      <Accordion title="Option A: Environment Variables">
        settings.gradle.kts (or build.gradle.kts):

        ```kotlin theme={null}
        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:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        Set environment variables before building:

        ```bash theme={null}
        export GITHUB_USER="your-github-username"
        export GITHUB_TOKEN="your-personal-access-token"
        ```
      </Accordion>

      <Accordion title="Option B: gradle.properties (Local Development)">
        Add credentials to your global Gradle properties file:

        \~/.gradle/gradle.properties:

        ```properties theme={null}
        gpr.user=your-github-username
        gpr.token=ghp_xxxxxxxxxxxx
        ```

        settings.gradle.kts:

        ```kotlin theme={null}
        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:

        ```kotlin theme={null}
        dependencies {
            implementation("dev.insforge:insforge-kotlin:0.1.6")
        }
        ```

        <Note>
          The `~/.gradle/gradle.properties` file is stored outside your project, so credentials won't be accidentally committed to version control.
        </Note>
      </Accordion>
    </AccordionGroup>
  </Tab>
</Tabs>

2. Initialize InsForge SDK

```kotlin theme={null}
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)
}
```

3. Enable Logging (Optional)

For debugging, you can configure the SDK log level:

```kotlin theme={null}
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                                       |
| --------- | ------------------------------------------------- |
| `NONE`    | No logging (default, recommended for production)  |
| `ERROR`   | Only errors                                       |
| `WARN`    | Warnings and errors                               |
| `INFO`    | Informational messages                            |
| `DEBUG`   | Debug info (request method, URL, response status) |
| `VERBOSE` | Full details (headers, request/response bodies)   |

<Note>
  Use `NONE` or `ERROR` in production to avoid exposing sensitive data in logs.
</Note>

### Android Initialization

1. Add Chrome Custom Tabs dependency to your `build.gradle.kts`:

```kotlin theme={null}
dependencies {
    implementation("androidx.browser:browser:1.9.0")
}
```

2. Initialize InsForge SDK (With Chrome Custom Tabs and Session Storage)

```kotlin theme={null}
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)
    }
}
```

3. Use Jetpack DataStore for Session Storage (Optional)

```kotlin theme={null}
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()

Crear una nueva cuenta de usuario con correo electrónico y contraseña.

### Parámetros

* `email` (String) - Dirección de correo electrónico del usuario
* `password` (String) - Contraseña del usuario
* `name` (String?, optional) - Nombre de visualización del usuario

### Devuelve

```kotlin theme={null}
SignUpResponse
```

### SignUpResponse

```kotlin theme={null}
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
)
```

### Ejemplo (Flujo completo con verificación)

```kotlin theme={null}
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 email
    suspend fun resendVerificationEmail(email: String) {
        try {
            client.auth.resendVerificationEmail(email = email)
            // Show success message
        } catch (e: InsforgeHttpException) {
            _uiState.value = AuthUiState.Error("Failed to resend verification email")
        }
    }
}

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()
}
```

### Verificación de correo electrónico

Para los usuarios que se registran con correo electrónico, el backend de InsForge proporciona tres opciones:

1. **Sin verificación de correo electrónico** - Los usuarios pueden iniciar sesión inmediatamente después del registro. `SignUpResponse` tendrá `accessToken != null`.
2. **Verificación basada en enlaces** - Los usuarios deben abrir su correo electrónico y hacer clic en el enlace de verificación antes de poder iniciar sesión.
3. **Verificación basada en código** - El backend de InsForge envía un código de verificación de 6 dígitos al correo electrónico del usuario. La aplicación cliente necesita mostrar una pantalla de verificación donde los usuarios puedan ingresar el código, luego llamar a `verifyEmail(email, code)` para completar la verificación. Solo después de esto, los usuarios pueden iniciar sesión con correo electrónico + contraseña.

Cuando `requireEmailVerification` es `true`, la respuesta tendrá:

* `accessToken = null`
* `user = null`
* `requireEmailVerification = true`

Esto indica que la verificación mediante la opción 2 o 3 es necesaria antes de que el usuario pueda iniciar sesión.

### Métodos relacionados

| Método                           | Descripción                                          |
| -------------------------------- | ---------------------------------------------------- |
| `verifyEmail(email, code)`       | Verificar correo electrónico con código de 6 dígitos |
| `resendVerificationEmail(email)` | Reenviar correo electrónico de verificación          |

***

## signIn()

Iniciar sesión con un usuario existente usando correo electrónico y contraseña.

### Ejemplo

```kotlin theme={null}
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}")
}
```

### Verificación de correo electrónico

Si la respuesta de inicio de sesión es:

```json theme={null}
{"error":"FORBIDDEN","message":"Email verification required","statusCode":403,"nextActions":"Please verify your email address before logging in"}
```

Esto indica que la verificación mediante la opción 2 o 3 (enlace o código, consulta [signUp()](#verificación-de-correo-electrónico)) es necesaria antes de que el usuario pueda iniciar sesión.

***

## signOut()

Cerrar la sesión del usuario actual.

### Ejemplo

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

***

## signInWithOAuthPage()

Inicia sesión directamente con un proveedor OAuth específico. Este método abre la página de autenticación del proveedor OAuth directamente en el navegador del sistema.

### Proveedores admitidos

```kotlin theme={null}
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")
}
```

### Parámetros

* `provider` (`OAuthProvider`) - El proveedor OAuth con el que autenticarse
* `redirectUri` (`String`) - URL de devolución de llamada donde InsForge redirigirá después de la autenticación

### Devuelve

```kotlin theme={null}
String  // The OAuth authorization URL (also opens in browser automatically)
```

### Ejemplo

1. Configurar BrowserLauncher

Al crear el cliente de InsForge, configura el `browserLauncher` para manejar la apertura de URLs:

```kotlin theme={null}
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
    }
}
```

2. Configurar devolución de llamada de App Link / Deep Link

Configura tu actividad de devolución de llamada en AndroidManifest.xml:

* Opción A: Esquema de URL personalizado (para desarrollo)

```xml theme={null}
<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>
```

* Opción B: App Links (para producción)

```xml theme={null}
<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>
```

3. Iniciar inicio de sesión con OAuth con proveedor específico

```kotlin theme={null}
// Start OAuth flow with Google
fun startGoogleLogin() {
    lifecycleScope.launch {
        val authUrl = client.auth.signInWithOAuthPage(
            OAuthProvider.GOOGLE,
            "yourapp://auth/callback"
        )
        // Browser opens automatically via browserLauncher
    }
}
```

4. Manejar devolución de llamada de OAuth

```kotlin theme={null}
class AuthCallbackActivity : AppCompatActivity() {
    // Example: read the shared InsForge client from your Application class.
    private val client by lazy { (application as MyApp).insforgeClient }

    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 {
                    val result = client.auth.handleAuthCallback(uri.toString())

                    Toast.makeText(
                        this@AuthCallbackActivity,
                        "Success: ${result.email}",
                        Toast.LENGTH_SHORT
                    ).show()

                    startActivity(Intent(this@AuthCallbackActivity, MainActivity::class.java))
                    finish()
                } catch (e: Exception) {
                    Toast.makeText(
                        this@AuthCallbackActivity,
                        "Failed: ${e.message}",
                        Toast.LENGTH_LONG
                    ).show()
                    finish()
                }
            }
        }
    }
}
```

`client` debe provenir de la misma instancia compartida del cliente de InsForge que utilizas en otros lugares de la aplicación, como un singleton de `Application` o tu contenedor de inyección de dependencias.

***

## getCurrentUser()

Obtener el usuario autenticado actual del servidor. Esta es una función suspensiva que realiza una solicitud de red.

### Devuelve

```kotlin theme={null}
CurrentUserResponse  // Contains user data from server
```

### Ejemplo

```kotlin theme={null}
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}")
}
```

<Note>
  Este método realiza una solicitud de red para obtener datos del usuario. Para acceder al estado del usuario almacenado en caché localmente, utiliza `currentUser` StateFlow.
</Note>

***

## updateProfile()

Actualizar el perfil del usuario actual.

### Parámetros

* `profile` (`Map\<String, Any\>`) - Campos de perfil a actualizar

### Devuelve

```kotlin theme={null}
ProfileResponse  // Updated profile data
```

### Ejemplo

```kotlin theme={null}
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}")
}
```

***

## Restablecimiento de contraseña

InsForge admite dos métodos de restablecimiento de contraseña, configurados en el backend:

* **Método de código**: El usuario recibe un código de 6 dígitos por correo electrónico, lo verifica para obtener un token de restablecimiento y luego restablece la contraseña
* **Método de enlace**: El usuario recibe un enlace mágico por correo electrónico que contiene el token de restablecimiento y luego restablece la contraseña directamente

### sendPasswordReset()

Envía un correo electrónico de restablecimiento de contraseña al usuario. El correo electrónico contendrá un código de 6 dígitos o un enlace mágico según la configuración del backend.

#### Parámetros

* `email` (String) - Dirección de correo electrónico del usuario

#### Ejemplo

```kotlin theme={null}
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()

Intercambia un código de restablecimiento de 6 dígitos por un token de restablecimiento. **Este método solo se usa con el flujo de restablecimiento basado en código.**

#### Parámetros

* `email` (String) - Dirección de correo electrónico del usuario
* `code` (String) - Código numérico de 6 dígitos recibido por correo electrónico

#### Devuelve

```kotlin theme={null}
ResetTokenResponse
```

#### ResetTokenResponse

```kotlin theme={null}
data class ResetTokenResponse(
    /** Reset token to use with resetPassword() */
    val token: String,
    /** Token expiration timestamp */
    val expiresAt: String?
)
```

#### Ejemplo

```kotlin theme={null}
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()

Restablecer la contraseña del usuario usando un token de restablecimiento.

#### Parámetros

* `newPassword` (String) - Nueva contraseña que cumple con los requisitos configurados
* `otp` (String) - Token de restablecimiento (de `exchangeResetPasswordToken()` para flujo de código, o de URL de enlace mágico para flujo de enlace)

#### Ejemplo

```kotlin theme={null}
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}")
}
```

***

## Manejo de errores

```kotlin theme={null}
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}")
}
```

### Códigos de error comunes

| Código de error       | Descripción                                           |
| --------------------- | ----------------------------------------------------- |
| `INVALID_CREDENTIALS` | El correo electrónico o la contraseña son incorrectos |
| `USER_NOT_FOUND`      | No existe ningún usuario con este correo electrónico  |
| `EMAIL_NOT_VERIFIED`  | Verificación de correo electrónico requerida          |
| `INVALID_EMAIL`       | Formato de correo electrónico no válido               |
| `WEAK_PASSWORD`       | La contraseña no cumple los requisitos                |
| `USER_ALREADY_EXISTS` | El correo electrónico ya está registrado              |
| `SESSION_EXPIRED`     | La sesión ha caducado, es necesario reiniciar sesión  |
