---
title: "sdk/ios"
description: "Swift SDK for iOS and macOS using ASWebAuthenticationSession, PKCE S256 authorization code flow, and Keychain token storage."
locale: "en"
---

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

# sdk/ios

## Status

Package status is **Implemented and verified locally**. The Swift unit-test suite passes on macOS for the iOS package. Simulator or device behavior and a real IdP round-trip on a running Vonvon instance remain pending manual verification. This page documents implemented behavior; it is not a production-readiness claim.

Registry status: UNPUBLISHED. Install this SDK only from the repository source checkout; do not use an external package registry.

## Requirements

- iOS 16+ / macOS 13+
- Swift 5.9+ and Xcode 15+
- No third-party dependencies — uses Apple system frameworks only

## Installation

Add the package via Swift Package Manager in Xcode (File -&gt; Add Package Dependencies) or directly in `Package.swift`:

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

## Quick start

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

## Core API

| Method | Description |
| --- | --- |
| `configure(options:)` | Initialize with issuer, clientId, redirectUri, scopes. Call before all others. |
| `signIn(options:) async throws` | Open ASWebAuthenticationSession with PKCE S256 authorization URL. Returns when the browser session ends. |
| `handleRedirect(url:) async throws -> VonvonSession` | Validate OAuth state, exchange authorization code at the token endpoint, persist tokens to Keychain, and return a session. |
| `getSession() async throws -> VonvonSession?` | Return the current unexpired iOS session; expired token state is cleared and the method returns nil. |
| `getAccessToken(forceRefresh:) async throws -> String` | Return the current unexpired access token. The SDK rejects offline\_access until DPoP is implemented; expiry requires reauthorization. |
| `signOut(callEndSession:) async throws` | Clear Keychain tokens. Pass true to call the end\_session endpoint via the browser. |
| `setTokenStorage(_:) throws` | Replace the default KeychainTokenStorage with a custom TokenStorageAdapter implementation. |

## Storage adapter

The default storage uses Keychain with `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` — tokens are not synced to iCloud Keychain. Implement the `TokenStorageAdapter` protocol to use an enterprise Keychain policy:

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

## Security

- Public client — no client secret stored or transmitted.
- PKCE S256 only. Server rejects plain challenge method.
- Random OAuth state generated per request; validated on redirect to prevent CSRF.
- PKCE code\_verifier written to Keychain only for the duration of the authorization flow and deleted immediately after the code exchange.
- ASWebAuthenticationSession launched with prefersEphemeralWebBrowserSession = true to avoid sharing browser cookies across apps.

## Known limitations

- JWKS-backed ES256/RS256 ID token verification, nonce validation, and end\_session logout are implemented and locally tested. A real IdP round-trip on an iOS device or simulator is still required before L4 support.
- Keychain behavior must be verified in an Xcode device or simulator test run.

Source: https://vonvon.id/sdks/ios/index.mdx
