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

# Authentication

> OAuth 2.1 with ASWebAuthenticationSession, Keychain helpers, token storage

## Authentication patterns

API keys are the simplest path — embed or seed via `fromKeychain(...)` and move on. Third-party apps that want per-user consent use [OAuth 2.1 with PKCE](/api/authentication#oauth-2-1) instead. The SDK doesn't ship a built-in OAuth client — it's an integration pattern — but the Keychain helpers handle token storage and rotation once you've exchanged for tokens.

### OAuth 2.1 with ASWebAuthenticationSession

Four moving parts: `ASWebAuthenticationSession` for the authorize redirect, `URLSession` for the token exchange, the SDK's Keychain helpers for persistence, and a catch on `UnauthorizedError` to trigger refresh.

**1. Launch the authorize flow.** Generate a PKCE verifier + challenge, build `/auth/authorize`, and let `ASWebAuthenticationSession` handle the redirect back.

```swift theme={null}
import AuthenticationServices
import CryptoKit

let verifier = randomURLSafeBase64(bytes: 32)
let challenge = Data(SHA256.hash(data: Data(verifier.utf8)))
  .base64EncodedString()
  .replacingOccurrences(of: "=", with: "")
  .replacingOccurrences(of: "+", with: "-")
  .replacingOccurrences(of: "/", with: "_")

var authorize = URLComponents(string: "\(serverURL)/auth/authorize")!
authorize.queryItems = [
  .init(name: "client_id",             value: clientId),
  .init(name: "redirect_uri",          value: "\(scheme)://callback"),
  .init(name: "response_type",         value: "code"),
  .init(name: "scope",                 value: "core.note:read core.note:write"),
  .init(name: "state",                 value: randomURLSafeBase64(bytes: 16)),
  .init(name: "code_challenge",        value: challenge),
  .init(name: "code_challenge_method", value: "S256"),
]

let session = ASWebAuthenticationSession(
  url: authorize.url!,
  callbackURLScheme: scheme
) { callbackURL, error in
  // Parse `code` and verify `state` round-trips, then POST /auth/token below.
}
session.presentationContextProvider = contextProvider
session.start()
```

**2. Exchange the code for tokens** via `POST /auth/token` with `grant_type=authorization_code`, the `code`, and the original `code_verifier`. The response carries `access_token` (`marfa_at_...`), `refresh_token` (`marfa_rt_...`), and `expires_in`.

**3. Persist the access token via the SDK's Keychain helper** — it becomes the API key for subsequent calls. Store the refresh token separately (same service, different account).

```swift theme={null}
let client = MarfaClient(url: serverURL, apiKey: tokens.accessToken)
try await client.saveToKeychain(service: "marfa.oauth", account: "access_token")
// refresh_token stored with account: "refresh_token" via SecureStorage of your choosing
```

On subsequent launches, rehydrate without the auth flow:

```swift theme={null}
let client = try await MarfaClient.fromKeychain(
  service: "marfa.oauth",
  account: "access_token",
  url: serverURL
)
```

**4. Refresh on expiry.** Any SDK call can throw `UnauthorizedError` when the access token expires. Catch it, exchange the refresh token for a new pair, overwrite the Keychain entries, and retry:

```swift theme={null}
do {
  return try await client.items.list(filters: .init(type: "core.note"))
} catch is UnauthorizedError {
  try await refreshTokens()  // POST /auth/token, grant_type=refresh_token
  return try await client.items.list(filters: .init(type: "core.note"))
}
```

<Info>
  Refresh tokens rotate on every exchange — the prior refresh token is invalidated. Reusing a rotated token returns `400 token_reuse_detected`; the app must restart the authorize flow. Store the latest pair atomically.
</Info>

<Warning>
  `ASWebAuthenticationSession` requires a UI host — a `UIWindow` on iOS, `NSWindow` on macOS. Server-side Swift, background extensions, and command-line tools cannot run this flow; they must use an API key.
</Warning>

See [Authentication → OAuth 2.1](/api/authentication#oauth-2-1) for the full endpoint reference, scope grammar, consent-screen behavior, and refresh-token rotation rules.
