---
title: "sdk/android"
description: "适用于 Android 的 Kotlin SDK，使用 Chrome Custom Tabs、PKCE S256 授权码流程和 Keystore 支持的 EncryptedSharedPreferences token 存储。"
locale: "zh-Hans"
---

> Documentation Index
> Fetch the relevant documentation index at: https://vonvon.id/zh-hans/sdks/llms.txt
> Use this file to discover all available pages before exploring further.

# sdk/android

## 状态

包状态为**已在本地实现并验证**。JVM 单元测试套件已通过，覆盖 PKCE、state 和 nonce 处理、Guest 能力及存储契约。EncryptedSharedPreferences、Chrome Custom Tabs、App Links 和真实 IdP 往返仍需设备或模拟器。本页记录的是已实现行为，不代表已具备生产就绪状态。

Registry 状态:UNPUBLISHED。此 SDK 只能从仓库源码 checkout 安装；不要使用外部 package registry。

## 要求

- Android API 26+（Android 8.0）
- Kotlin 1.9+ 和 AndroidX

## 安装

在应用模块的 build.gradle.kts 中添加依赖：

```kotlin
// settings.gradle.kts
includeBuild("../vonvon/sdk/android")

// app/build.gradle.kts
dependencies {
implementation("dev.vonvon:vonvon-android:0.1.0-alpha.0")
}
```

## 清单配置

注册带 intent-filter 的 callback Activity。生产环境推荐使用 App Links（带 autoVerify 的 HTTPS scheme）而非自定义 scheme：

```xml
<!-- AndroidManifest.xml -->
<activity android:name=".AuthCallbackActivity" 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="yourapp.example.com"
          android:pathPrefix="/auth/callback" />
</intent-filter>
</activity>
```

## 快速开始

```kotlin
import dev.vonvon.sdk.Vonvon
import dev.vonvon.sdk.model.VonvonConfig

// 1. Initialize in Application.onCreate. offline_access is rejected until DPoP is implemented.
Vonvon.configure(
context = this,
config = VonvonConfig(
    issuer = "https://vonvon.id",
    clientId = "your_client_id",
    redirectUri = "https://yourapp.example.com/auth/callback",
    scopes = listOf("openid", "profile", "email"),
)
)

// 2. Sign in (opens Chrome Custom Tabs)
lifecycleScope.launch { Vonvon.signIn(requireContext()) }

// 3. Handle redirect in AuthCallbackActivity
val session = Vonvon.handleRedirect(intent.data.toString())

// 4. Read the current unexpired session. Expiry requires reauthorization.
val session = Vonvon.getSession()

// 5. Get the current unexpired access token.
val token = Vonvon.getAccessToken()

// 6. Clear local state and optionally open end_session.
Vonvon.signOut(context = this, openEndSession = true)
```

## 核心 API

| 方式 | 签名 |
| --- | --- |
| `configure` | `fun configure(context: Context, config: VonvonConfig)` |
| `signIn` | `suspend fun signIn(context: Context, options: SignInOptions? = null)` |
| `handleRedirect` | `suspend fun handleRedirect(url: String): VonvonSession` |
| `getSession` | `suspend fun getSession(): VonvonSession?` |
| `getAccessToken` | `suspend fun getAccessToken(options: GetAccessTokenOptions? = null): String` |
| `signOut` | `suspend fun signOut(context: Context? = null, openEndSession: Boolean = false)` |
| `setTokenStorage` | `fun setTokenStorage(adapter: TokenStorageAdapter)` |

## 错误类型

所有 SDK 错误均为 sealed class VonvonException 的子类型：

| 子类 | 触发 |
| --- | --- |
| `NotConfigured` | configure() 未被调用 |
| `UserCancelled` | 用户在未完成时关闭了 Custom Tabs |
| `StateMismatch` | OAuth state 不匹配——可能存在 CSRF |
| `TokenExchangeFailed` | Token 端点返回了错误 |
| `TokenValidationFailed` | ID token signature 或 claims 验证失败 |
| `NoSession` | 在已登出状态下调用会话方法 |

## 安全

- 公共客户端——不存储或传输客户端密钥。
- 仅 PKCE S256。服务器拒绝 plain challenge 方式。
- 由 Android Keystore（AES-256-GCM）支持的 EncryptedSharedPreferences 保护静态 token 存储。
- 每次请求生成随机 OAuth state；在重定向时验证以防 CSRF。

## 已知限制

- 基于 JWKS 的 ID token 验签已实现并完成本地单元测试。Android 设备或模拟器和真实 IdP 验证前仍不具备 L4 支持。
- 没有机制检测用户在未完成授权时关闭 Custom Tabs。
- 仅支持单账户——存储层使用固定密钥。

Source: https://vonvon.id/zh-hans/sdks/android/index.mdx
