状态
包状态为 已实现 · 本地已验证。JVM 单元测试(24 个通过)覆盖 PKCE 生成、OAuth state 和内存存储。EncryptedSharedPreferences(Keystore AES-256-GCM)、Chrome Custom Tabs 和 App Links 行为需要真实 Android 设备或模拟器验证。IdP 往返验证待人工核实。本页面记录已实现的行为,不代表生产就绪声明。
要求
- Android API 26+(Android 8.0)
- Kotlin 1.9+ 和 AndroidX
安装
在应用模块的 build.gradle.kts 中添加依赖:
dependencies {
implementation("dev.xid:xid-android:0.1.0-alpha")
}
// Local development: add to settings.gradle.kts
includeBuild("../sdk/android")清单配置
注册带 intent-filter 的 callback Activity。生产环境推荐使用 App Links(带 autoVerify 的 HTTPS scheme)而非自定义 scheme:
<!-- 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>快速开始
import dev.xid.sdk.Xid
import dev.xid.sdk.model.XidConfig
// 1. Initialize in Application.onCreate
Xid.configure(
context = this,
config = XidConfig(
issuer = "https://xid.dev",
clientId = "your_client_id",
redirectUri = "https://yourapp.example.com/auth/callback",
scopes = listOf("openid", "profile", "email", "offline_access"),
)
)
// 2. Sign in (opens Chrome Custom Tabs)
lifecycleScope.launch { Xid.signIn(requireContext()) }
// 3. Handle redirect in AuthCallbackActivity
val session = Xid.handleRedirect(intent.data.toString())
// 4. Get current session (auto-refreshes near expiry)
val session = Xid.getSession()
// 5. Get access token
val token = Xid.getAccessToken()
// 6. Sign out
Xid.signOut(context = this, openEndSession = true)核心 API
| 方式 | 签名 |
|---|---|
configure |
fun configure(context: Context, config: XidConfig) |
signIn |
suspend fun signIn(context: Context, options: SignInOptions? = null) |
handleRedirect |
suspend fun handleRedirect(url: String): XidSession |
getSession |
suspend fun getSession(): XidSession? |
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 XidException 的子类型:
| 子类 | 触发 |
|---|---|
NotConfigured |
configure() 未被调用 |
UserCancelled |
用户在未完成时关闭了 Custom Tabs |
StateMismatch |
OAuth state 不匹配——可能存在 CSRF |
TokenExchangeFailed |
Token 端点返回了错误 |
TokenRefreshFailed |
refresh token 已过期或被吊销 |
NoSession |
在已登出状态下调用会话方法 |
安全
- 公共客户端——不存储或传输客户端密钥。
- 仅 PKCE S256。服务器拒绝 plain challenge 方式。
- 由 Android Keystore(AES-256-GCM)支持的 EncryptedSharedPreferences 保护静态 token 存储。
- 每次请求生成随机 OAuth state;在重定向时验证以防 CSRF。
已知限制
- 基于 JWKS 的 ID token 验签已实现并完成本地单元测试。Android 设备或模拟器和真实 IdP 验证前仍不具备 L4 支持。
- 没有机制检测用户在未完成授权时关闭 Custom Tabs。
- 仅支持单账户——存储层使用固定密钥。