docs: Update documentation from swift SDK changes (#47024)

## Summary

Updates docs based on stable releases in `supabase/supabase-swift`.

## Changes analyzed

- **SDK**: swift
- **Repo**: https://github.com/supabase/supabase-swift
- **Stable tag range**: `v2.47.2...v2.48.0`
- **Commits**:
`716e0de6e7e0ec8eba54c6c4dfb8e980b3d51370...e5020ae5a1d01c46cc60fb5eb01157666d7214b6`

## Documentation updates

### `apps/docs/spec/supabase_swift_v2.yml`
- Added `sign-in-with-passkey` entry —
`signInWithPasskey(presentationAnchor:)` high-level helper (iOS
16+/macOS 13+, experimental)
- Added `register-passkey` entry —
`registerPasskey(presentationAnchor:)` high-level helper (iOS 16+/macOS
13+, experimental)
- Added `passkey-api` group stub with 7 lower-level method entries:
  - `passkey-list` — `listPasskeys()`
  - `passkey-update` — `renamePasskey(id:friendlyName:)`
  - `passkey-delete` — `deletePasskey(id:)`
  - `passkey-start-registration` — `getPasskeyRegistrationOptions()`
- `passkey-verify-registration` —
`verifyPasskeyRegistration(challengeId:credentialResponse:)`
  - `passkey-start-authentication` — `getPasskeyAuthenticationOptions()`
- `passkey-verify-authentication` —
`verifyPasskeyAuthentication(challengeId:credentialResponse:)`
- Updated `auth-mfa-api` overview notes to mention WebAuthn MFA
(experimental)
- Updated `mfa-enroll` notes and added `enrollWebAuthnFactor` example
- Updated `mfa-verify` notes and added `verifyWebAuthnFactor` example

### `apps/docs/content/guides/auth/passkeys.mdx`
- Added Swift tabs to: Enable in the client, Register a passkey, Sign in
with a passkey, Two-step API, Manage passkeys
- Updated SDK version note to include `supabase-swift` v2.48.0
- Updated reference links to include Swift

All new Swift passkey APIs are gated behind `@_spi(Experimental) import
Supabase`.

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

## Documentation
* Expanded Swift SDK passkeys documentation to cover enabling,
registering, signing in, managing, and Admin-related notes.
* Added experimental Swift guidance for WebAuthn/passkeys, including
updated minimum SDK version requirements.
* Extended MFA to support WebAuthn/passkeys enrollment and verification
(in addition to existing methods).
* Added reference documentation for low-level passkey APIs to support
custom authentication flows.

## Chores
* Updated documentation linting spell-check allow list to include
**visionOS**.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Guilherme SouzaandClaude authored and GitHub committed 2026-06-17 08:05:05 -03:00
1 parent de76c83238
commit 0c9d90051f
3 files changed
+331 -10

No files matched your search

+107 -8
View File
@@ -14,7 +14,7 @@ Passkey support is experimental. The API may change without notice. You must exp
<Admonition type="note">
**Requires `@supabase/supabase-js` v2.105.0 and later, or `supabase_flutter` v2.15.0 and later.** Upgrade your client library to use passkey authentication.
**Requires `@supabase/supabase-js` v2.105.0 and later, `supabase_flutter` v2.15.0 and later, or `supabase-swift` v2.48.0 and later.** Upgrade your client library to use passkey authentication.
</Admonition>
@@ -135,6 +135,19 @@ final supabase = Supabase.instance.client;
Platform setup that the library cannot do for you (Associated Domains on iOS/macOS, Digital Asset Links on Android, and including the [`passkeys`](https://pub.dev/packages/passkeys) web SDK in `index.html` on web) is documented in the `supabase_flutter` package README.
</TabPanel>
<TabPanel id="swift" label="Swift">
The Swift SDK gates passkey support behind `@_spi(Experimental)`. Add this import to every file that uses passkey APIs:
```swift
@_spi(Experimental) import Supabase
```
The `SupabaseClient` itself needs no extra configuration — the experimental SPI is enabled at the import site, not at client initialization.
Platform setup the library cannot perform for you (Associated Domains entitlement and a relying-party server with HTTPS) must be configured in your Xcode project. Refer to [Apple's documentation on passkeys](https://developer.apple.com/documentation/authenticationservices/public-private_key_authentication/supporting_passkeys) for details.
</TabPanel>
</Tabs>
@@ -180,6 +193,25 @@ try {
}
```
</TabPanel>
<TabPanel id="swift" label="Swift">
Available on iOS 16+, macOS 13+, and visionOS 1+. Requires `@_spi(Experimental) import Supabase`.
```swift
do {
let passkey = try await supabase.auth.registerPasskey(
presentationAnchor: view.window!
)
print("Registered passkey \(passkey.id)")
} catch {
// AuthError from the server, or user cancelled the native UI.
print(error)
}
```
For lower-level control (or on tvOS/watchOS), use `getPasskeyRegistrationOptions()` + `verifyPasskeyRegistration(challengeId:credentialResponse:)` from the [Auth Passkey](/docs/reference/swift/auth-passkey-api) reference.
</TabPanel>
</Tabs>
@@ -195,7 +227,7 @@ The returned passkey contains the new credential's metadata:
A friendly name is automatically derived from the authenticator's Authenticator Attestation GUID (AAGUID). For example, `iCloud Keychain`, `Google Password Manager`, `1Password`. Users can rename their passkey afterwards — see [Manage passkeys](#manage-passkeys).
See the `registerPasskey` reference ([JavaScript](/docs/reference/javascript/auth-registerpasskey) · [Dart](/docs/reference/dart/auth-registerpasskey)) for the full API.
See the `registerPasskey` reference ([JavaScript](/docs/reference/javascript/auth-registerpasskey) · [Dart](/docs/reference/dart/auth-registerpasskey) · [Swift](/docs/reference/swift/auth-registerpasskey)) for the full API.
## Sign in with a passkey
@@ -234,10 +266,29 @@ try {
}
```
</TabPanel>
<TabPanel id="swift" label="Swift">
Available on iOS 16+, macOS 13+, and visionOS 1+. Requires `@_spi(Experimental) import Supabase`.
```swift
do {
let response = try await supabase.auth.signInWithPasskey(
presentationAnchor: view.window!
)
// response.session and response.user are set; the client also fires a signedIn event.
print("Signed in as \(response.user?.email ?? "")")
} catch {
print(error)
}
```
For lower-level control (or on tvOS/watchOS), use `getPasskeyAuthenticationOptions()` + `verifyPasskeyAuthentication(challengeId:credentialResponse:)` from the [Auth Passkey](/docs/reference/swift/auth-passkey-api) reference.
</TabPanel>
</Tabs>
See the `signInWithPasskey` reference ([JavaScript](/docs/reference/javascript/auth-signinwithpasskey) · [Dart](/docs/reference/dart/auth-signinwithpasskey)) for the full API.
See the `signInWithPasskey` reference ([JavaScript](/docs/reference/javascript/auth-signinwithpasskey) · [Dart](/docs/reference/dart/auth-signinwithpasskey) · [Swift](/docs/reference/swift/auth-signinwithpasskey)) for the full API.
## Two-step API
@@ -307,12 +358,41 @@ final AuthResponse res = await supabase.auth.passkey.verifyAuthentication(
);
```
</TabPanel>
<TabPanel id="swift" label="Swift">
Requires `@_spi(Experimental) import Supabase`. Works on all Apple platforms (iOS, macOS, tvOS, watchOS, visionOS).
Registration:
```swift
let options = try await supabase.auth.getPasskeyRegistrationOptions()
// Run the platform authenticator yourself (e.g. via ASAuthorizationController).
let credential: AnyJSON = try await runRegistrationCeremony(options.options)
let passkey = try await supabase.auth.verifyPasskeyRegistration(
challengeId: options.challengeId,
credentialResponse: credential
)
```
Authentication:
```swift
let options = try await supabase.auth.getPasskeyAuthenticationOptions()
// Run the platform authenticator yourself (e.g. via ASAuthorizationController).
let credential: AnyJSON = try await runAuthenticationCeremony(options.options)
let response = try await supabase.auth.verifyPasskeyAuthentication(
challengeId: options.challengeId,
credentialResponse: credential
)
```
</TabPanel>
</Tabs>
The `options` field returned from `startRegistration` and `startAuthentication` matches the [WebAuthn `PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions) and [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions) shapes (with `ArrayBuffer` fields encoded as base64url).
The `options` field returned from the start methods matches the [WebAuthn `PublicKeyCredentialCreationOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialcreationoptions) and [`PublicKeyCredentialRequestOptions`](https://www.w3.org/TR/webauthn-3/#dictdef-publickeycredentialrequestoptions) shapes (with `ArrayBuffer` fields encoded as base64url).
See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api)) for the full API.
See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api) · [Swift](/docs/reference/swift/auth-passkey-api)) for the full API.
## Manage passkeys
@@ -359,12 +439,31 @@ await supabase.auth.passkey.update(
await supabase.auth.passkey.delete(passkeyId: passkeys.first.id);
```
</TabPanel>
<TabPanel id="swift" label="Swift">
Requires `@_spi(Experimental) import Supabase`.
```swift
// List
let passkeys: [PasskeyListItem] = try await supabase.auth.listPasskeys()
// Rename
let updated = try await supabase.auth.renamePasskey(
id: passkeys.first!.id,
friendlyName: "Work laptop"
)
// Delete
try await supabase.auth.deletePasskey(id: passkeys.first!.id)
```
</TabPanel>
</Tabs>
`friendlyName` is limited to 120 characters. `last_used_at` is updated each time the passkey is used to sign in.
`friendlyName` is limited to 120 characters. `lastUsedAt` is updated each time the passkey is used to sign in.
See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api)) for the full API.
See the `auth.passkey` reference ([JavaScript](/docs/reference/javascript/auth-passkey-api) · [Dart](/docs/reference/dart/auth-passkey-api) · [Swift](/docs/reference/swift/auth-passkey-api)) for the full API.
## Admin API
@@ -410,7 +509,7 @@ await supabase.auth.admin.passkey.deletePasskey(
</TabPanel>
</Tabs>
See the `auth.admin.passkey` reference ([JavaScript](/docs/reference/javascript/auth-admin-passkey-api) · [Dart](/docs/reference/dart/auth-admin-passkey-api)) for the full API.
See the `auth.admin.passkey` reference ([JavaScript](/docs/reference/javascript/auth-admin-passkey-api) · [Dart](/docs/reference/dart/auth-admin-passkey-api)) for the full API. The Swift SDK does not expose admin passkey methods.
## Error codes
+223 -2
View File
@@ -679,6 +679,50 @@ functions:
// Open the URL using your preferred method to complete sign-in process.
UIApplication.shared.open(url)
```
- id: sign-in-with-passkey
title: 'signInWithPasskey()'
notes: |
Signs the user in with a passkey (WebAuthn). Available on iOS 16+, macOS 13+, and visionOS 1+.
- Drives the full WebAuthn ceremony end to end: fetches assertion options from the server, presents the native passkey UI via `AuthenticationServices`, and verifies the assertion.
- Does not require an existing session. On success the session is persisted and a `signedIn` auth change event is emitted.
- For lower-level control (custom authenticator, tvOS, watchOS), use `getPasskeyAuthenticationOptions()` + `verifyPasskeyAuthentication()` instead.
- Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`. The API may change in future releases.
- Passkeys must be enabled for your project in the Dashboard under Authentication → Passkeys.
examples:
- id: sign-in-with-passkey
name: Sign in with a passkey
isSpotlight: true
code: |
```swift
// iOS 16+/macOS 13+ only. Must opt in: @_spi(Experimental) import Supabase
let response = try await supabase.auth.signInWithPasskey(
presentationAnchor: view.window!
)
let session = response.session
let user = response.user
```
- id: register-passkey
title: 'registerPasskey()'
notes: |
Registers a new passkey (WebAuthn credential) for the signed-in user. Available on iOS 16+, macOS 13+, and visionOS 1+.
- Drives the full WebAuthn ceremony end to end: fetches creation options from the server, presents the native passkey registration UI, and stores the credential.
- Requires an authenticated, non-anonymous user.
- For lower-level control, use `getPasskeyRegistrationOptions()` + `verifyPasskeyRegistration()` instead.
- Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`.
examples:
- id: register-passkey
name: Register a passkey for the current user
isSpotlight: true
code: |
```swift
// iOS 16+/macOS 13+ only. Must opt in: @_spi(Experimental) import Supabase
let passkey = try await supabase.auth.registerPasskey(
presentationAnchor: view.window!
)
print("Registered passkey \(passkey.id)")
```
- id: sign-out
title: 'signOut()'
notes: |
@@ -1217,16 +1261,19 @@ functions:
notes: |
This section contains methods commonly used for Multi-Factor Authentication (MFA) and are invoked behind the `supabase.auth.mfa` namespace.
Currently, we only support time-based one-time password (TOTP) as the 2nd factor. We don't support recovery codes but we allow users to enroll more than 1 TOTP factor, with an upper limit of 10.
TOTP (time-based one-time password) is the stable 2nd factor. WebAuthn / passkey as a 2nd factor is **experimental** — opt in with `@_spi(Experimental) import Supabase`. The first-factor passkey API lives in the `auth` (not `auth.mfa`) namespace; see the [Auth Passkey](/docs/reference/swift/auth-passkey-api) section.
We don't support recovery codes but we allow users to enroll more than 1 TOTP factor, with an upper limit of 10.
Having a 2nd TOTP factor for recovery frees the user of the burden of having to store their recovery codes somewhere. It also reduces the attack surface since multiple recovery codes are usually generated compared to just having 1 backup TOTP factor.
- id: mfa-enroll
title: 'mfa.enroll()'
notes: |
- Currently, `totp` is the only supported `factorType`. The returned `id` should be used to create a challenge.
- Supported factor types: `totp` (stable) and `webauthn` (**experimental** — opt in with `@_spi(Experimental) import Supabase`). The returned `id` should be used to create a challenge.
- To create a challenge, see [`mfa.challenge()`](/docs/reference/swift/auth-mfa-challenge).
- To verify a challenge, see [`mfa.verify()`](/docs/reference/swift/auth-mfa-verify).
- To create and verify a challenge in a single step, see [`mfa.challengeAndVerify()`](/docs/reference/swift/auth-mfa-challengeandverify).
- For a one-call WebAuthn enrollment on iOS 16+/macOS 13+, use `mfa.enrollWebAuthnFactor(friendlyName:presentationAnchor:)` instead.
examples:
- id: enroll-totp-factor
@@ -1250,6 +1297,17 @@ functions:
let secret = response.totp?.secret
let uri = response.totp?.uri
```
- id: enroll-webauthn-factor
name: Enroll a WebAuthn (passkey) factor (iOS 16+/macOS 13+, experimental)
code: |
```swift
// @_spi(Experimental) import Supabase
// enrollWebAuthnFactor drives the full ceremony: enroll → challenge → present native UI → verify.
let verifyResponse = try await supabase.auth.mfa.enrollWebAuthnFactor(
friendlyName: "My passkey",
presentationAnchor: view.window!
)
```
- id: mfa-challenge
title: 'mfa.challenge()'
notes: |
@@ -1271,6 +1329,7 @@ functions:
title: 'mfa.verify()'
notes: |
- To verify a challenge, please [create a challenge](/docs/reference/swift/auth-mfa-challenge) first.
- For WebAuthn factors on iOS 16+/macOS 13+, `mfa.verifyWebAuthnFactor(factorId:presentationAnchor:)` drives the full challenge + native UI + verify flow in one call (**experimental** — opt in with `@_spi(Experimental) import Supabase`).
examples:
- id: verify-challenge
name: Verify a challenge for a factor
@@ -1285,6 +1344,17 @@ functions:
)
)
```
- id: verify-webauthn-factor
name: Verify a WebAuthn factor (iOS 16+/macOS 13+, experimental)
code: |
```swift
// @_spi(Experimental) import Supabase
// verifyWebAuthnFactor drives the full ceremony: challenge → present native UI → verify.
let session = try await supabase.auth.mfa.verifyWebAuthnFactor(
factorId: "34e770dd-9ff9-416c-87fa-43b31d7ef225",
presentationAnchor: view.window!
)
```
- id: mfa-challenge-and-verify
title: 'mfa.challengeAndVerify()'
notes: |
@@ -1347,6 +1417,157 @@ functions:
```swift
let factors = try await supabase.auth.mfa.listFactors()
```
- id: passkey-api
title: 'Overview'
notes: |
Lower-level passkey methods for custom authenticator flows and platforms where `AuthenticationServices` is unavailable (tvOS, watchOS).
These methods handle the network side of the WebAuthn ceremony only — the caller is responsible for driving the platform authenticator between fetching options and submitting the credential response. For an end-to-end flow on iOS 16+/macOS 13+, prefer `signInWithPasskey(presentationAnchor:)` and `registerPasskey(presentationAnchor:)`.
Passkey support is **experimental**. Opt in with `@_spi(Experimental) import Supabase`. The API may change in future releases.
- id: passkey-list
title: 'listPasskeys()'
notes: |
Returns the list of passkeys registered to the signed-in user.
examples:
- id: list-passkeys
name: List the current user's passkeys
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let passkeys: [PasskeyListItem] = try await supabase.auth.listPasskeys()
```
- id: passkey-update
title: 'renamePasskey(id:friendlyName:)'
notes: |
Updates the friendly name of a passkey. Limited to 120 characters.
params:
- name: id
isOptional: false
type: String
description: ID of the passkey to rename.
- name: friendlyName
isOptional: false
type: String
description: New human readable name. Limited to 120 characters.
examples:
- id: rename-passkey
name: Rename a passkey
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let passkey = try await supabase.auth.renamePasskey(
id: "34e770dd-9ff9-416c-87fa-43b31d7ef225",
friendlyName: "Work laptop"
)
```
- id: passkey-delete
title: 'deletePasskey(id:)'
notes: |
Removes a passkey from the signed-in user's account.
params:
- name: id
isOptional: false
type: String
description: ID of the passkey to delete.
examples:
- id: delete-passkey
name: Delete a passkey
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
try await supabase.auth.deletePasskey(id: "34e770dd-9ff9-416c-87fa-43b31d7ef225")
```
- id: passkey-start-registration
title: 'getPasskeyRegistrationOptions()'
notes: |
Fetches credential creation options to register a new passkey for the signed-in user.
- Pass the returned `options` (W3C `PublicKeyCredentialCreationOptions`) to the platform authenticator.
- After running the authenticator, submit the result with `verifyPasskeyRegistration(challengeId:credentialResponse:)`.
examples:
- id: start-passkey-registration
name: Get passkey registration options
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let options: PasskeyRegistrationOptions = try await supabase.auth.getPasskeyRegistrationOptions()
// Hand options.options to the platform authenticator.
```
- id: passkey-verify-registration
title: 'verifyPasskeyRegistration(challengeId:credentialResponse:)'
notes: |
Stores a newly created passkey for the signed-in user.
params:
- name: challengeId
isOptional: false
type: String
description: The `challengeId` returned by `getPasskeyRegistrationOptions()`.
- name: credentialResponse
isOptional: false
type: AnyJSON
description: The W3C `RegistrationResponseJSON` credential produced by the authenticator.
examples:
- id: verify-passkey-registration
name: Verify a passkey registration
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let passkey: PasskeyListItem = try await supabase.auth.verifyPasskeyRegistration(
challengeId: options.challengeId,
credentialResponse: credential
)
```
- id: passkey-start-authentication
title: 'getPasskeyAuthenticationOptions()'
notes: |
Fetches assertion options to authenticate with a passkey.
- Does not require an existing session.
- Pass the returned `options` (W3C `PublicKeyCredentialRequestOptions`) to the platform authenticator.
- After running the authenticator, submit the result with `verifyPasskeyAuthentication(challengeId:credentialResponse:)`.
examples:
- id: start-passkey-authentication
name: Get passkey authentication options
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let options: PasskeyAuthenticationOptions = try await supabase.auth.getPasskeyAuthenticationOptions()
// Hand options.options to the platform authenticator.
```
- id: passkey-verify-authentication
title: 'verifyPasskeyAuthentication(challengeId:credentialResponse:)'
notes: |
Verifies a passkey assertion and establishes a session. On success the session is persisted and a `signedIn` auth change event is emitted.
params:
- name: challengeId
isOptional: false
type: String
description: The `challengeId` returned by `getPasskeyAuthenticationOptions()`.
- name: credentialResponse
isOptional: false
type: AnyJSON
description: The W3C `AuthenticationResponseJSON` assertion produced by the authenticator.
examples:
- id: verify-passkey-authentication
name: Verify a passkey sign in
isSpotlight: true
code: |
```swift
// @_spi(Experimental) import Supabase
let response: AuthResponse = try await supabase.auth.verifyPasskeyAuthentication(
challengeId: options.challengeId,
credentialResponse: credential
)
let session = response.session
let user = response.user
```
- id: admin-api
title: 'Overview'
notes: |
+1
View File
@@ -495,6 +495,7 @@ allow_list = [
"tokio",
"tsvector",
"tvOS",
"visionOS",
"uBlock Origin",
"unbilled",
"unpublish",