---
title: "sdk/ios"
description: "适用于 iOS 和 macOS 的 Swift SDK，使用 ASWebAuthenticationSession、PKCE S256 授权码流程和 Keychain 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/ios

## 状态

包状态为**已在本地实现并验证**。iOS 包的 Swift 单元测试套件已在 macOS 上通过。模拟器或设备行为，以及针对运行中 Vonvon 实例的真实 IdP 往返仍待人工验证。本页记录的是已实现行为，不代表已具备生产就绪状态。

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

## 要求

- iOS 16+ / macOS 13+
- Swift 5.9+ 和 Xcode 15+
- 无第三方依赖——仅使用 Apple 系统框架

## 安装

通过 Xcode 中的 Swift Package Manager（File -&gt; Add Package Dependencies）或直接在 `Package.swift` 中添加包：

```swift
// Package.swift
dependencies: [
.package(path: "../vonvon/sdk/ios"),
],
targets: [
.target(name: "YourApp", dependencies: [.product(name: "Vonvon", package: "ios")]),
]
```

## 快速开始

```swift
import Vonvon

// 1. Configure in @main App.init. offline_access is rejected until DPoP is implemented.
Vonvon.shared.configure(options: VonvonConfiguration(
issuer: URL(string: "https://vonvon.id")!,
clientId: "your_client_id",
redirectUri: URL(string: "com.example.app://auth/callback")!,
scopes: ["openid", "profile", "email"]
))

// 2. Sign in (opens ASWebAuthenticationSession)
try await Vonvon.shared.signIn()

// 3. Handle redirect in SceneDelegate
let session = try await Vonvon.shared.handleRedirect(url: callbackUrl)

// 4. Read the current unexpired session. Expiry requires reauthorization.
if let session = try await Vonvon.shared.getSession() {
let token = try await Vonvon.shared.getAccessToken()
}

// 5. Clear local state and optionally call end_session.
try await Vonvon.shared.signOut(callEndSession: true)
```

## 核心 API

| 方式 | 描述 |
| --- | --- |
| `configure(options:)` | 使用 issuer、clientId、redirectUri、scopes 初始化。在其他方法之前调用。 |
| `signIn(options:) async throws` | 以 PKCE S256 授权 URL 打开 ASWebAuthenticationSession。浏览器会话结束时返回。 |
| `handleRedirect(url:) async throws -> VonvonSession` | 校验 OAuth state，在 token 端点交换授权码，将 token 持久化到 Keychain，并返回会话。 |
| `getSession() async throws -> VonvonSession?` | 返回当前未过期的 iOS 会话；过期的 token state 会被清除，且该方法返回 nil。 |
| `getAccessToken(forceRefresh:) async throws -> String` | 返回当前未过期的 access token。在实现 DPoP 之前，SDK 会拒绝 offline\_access；token 到期后需要重新授权。 |
| `signOut(callEndSession:) async throws` | 清除 Keychain token。传入 true 可通过浏览器调用 end\_session 端点。 |
| `setTokenStorage(_:) throws` | 用自定义 TokenStorageAdapter 实现替换默认的 KeychainTokenStorage。 |

## 存储适配器

默认存储使用带 `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` 的 Keychain——token 不会同步到 iCloud Keychain。实现 `TokenStorageAdapter` 协议以使用企业 Keychain 策略：

```swift
struct EnterpriseKeychain: TokenStorageAdapter {
func save(key: String, value: String) throws { /* ... */ }
func load(key: String) throws -> String? { /* ... */ }
func delete(key: String) throws { /* ... */ }
}
try Vonvon.shared.setTokenStorage(EnterpriseKeychain())
```

## 安全

- 公共客户端——不存储或传输客户端密钥。
- 仅 PKCE S256。服务器拒绝 plain challenge 方式。
- 每次请求生成随机 OAuth state；在重定向时验证以防 CSRF。
- PKCE code\_verifier 仅在授权流程期间写入 Keychain，code 交换完成后立即删除。
- 以 prefersEphemeralWebBrowserSession = true 启动 ASWebAuthenticationSession，避免跨应用共享浏览器 cookie。

## 已知限制

- 已实现并在本地测试基于 JWKS 的 ES256/RS256 ID token 验证、nonce 验证和 end\_session logout。在达到 L4 支持之前，仍需在 iOS 设备或 simulator 上完成真实 IdP round-trip。
- 必须在 Xcode 设备或模拟器测试中验证 Keychain 行为。

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