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

# Swift sessions and access

> Restore wallet sessions, create or import wallets, issue wallet ID tokens, and manage direct or remote access.

An active session combines a selected wallet with an unexpired OMS credential. The SDK persists completed session metadata and restores it when you create `OMSWallet` again.

## Read restored session state

```swift theme={null}
let omsWallet = try OMSWallet(publishableKey: "YOUR_PUBLISHABLE_KEY")
let session = omsWallet.wallet.session

if let walletAddress = session.walletAddress {
    print("Restored wallet:", walletAddress)
    print("Expires:", session.expiresAt as Any)
    print("Identity:", session.auth?.email as Any)
}
```

`OMSWalletSessionState` contains completed-session metadata: `walletAddress`, `expiresAt`, and authentication metadata in `auth`. For OIDC, the metadata identifies `.idToken` or `.redirect`, the issuer, optional provider details, and an email when OMS returns one.

The request-signing credential is separate. Its non-extractable P-256 private key remains Keychain-managed and is not serialized into `OMSWalletSessionState`. Restoration succeeds only when the completed metadata is unexpired and its Keychain credential can be restored.

<Note>
  A manual `PendingWalletSelection` is not completed session metadata. It remains in memory only until your app activates a wallet.
</Note>

## Observe expiration

Expired sessions are not activated. Keep the returned observation alive while you need notifications. The callback runs on `MainActor`.

```swift theme={null}
let expiryObservation = omsWallet.wallet.addSessionExpiredObserver { event in
    print("Expired wallet:", event.session.walletAddress ?? "unknown")
    print("Expired at:", event.expiredAt)
}

// Cancel when the owner no longer needs notifications.
expiryObservation.cancel()
```

The event retains the expired session snapshot so you can choose the appropriate reauthentication UI.

## List, switch, and create wallets

These operations require the authenticated credential. `listWallets()` follows all server cursors and returns the complete list.

```swift theme={null}
let wallets = try await omsWallet.wallet.listWallets()

if let secondary = wallets.dropFirst().first {
    let selected = try await omsWallet.wallet.useWallet(
        walletId: secondary.id
    )
    print("Selected:", selected.walletAddress)
}
```

Create and activate another wallet for the same credential when your product requires one.

```swift theme={null}
let created = try await omsWallet.wallet.createWallet(
    reference: "trading"
)

print("Created:", created.wallet.address)
```

Both `useWallet` and `createWallet` replace the active wallet while preserving the current session expiry and authentication metadata.

Pass `walletType: .solana` to create a Solana wallet:

```swift theme={null}
let solana = try await omsWallet.wallet.createWallet(
    walletType: .solana,
    reference: "solana-primary"
)
```

## Import and activate a wallet

```swift theme={null}
let imported = try await omsWallet.wallet.importWallet(
    privateKey: .ethereum("0x..."), // Supply the key from your secure migration flow.
    reference: "imported-wallet"
)

print(imported.wallet.keyOrigin == .imported)
```

Ethereum imports accept 32 raw bytes or 64 hexadecimal digits with an optional `0x` prefix. Solana imports accept a 32-byte seed or 64-byte keypair as raw bytes, or the base58 encoding of either. The SDK encrypts the key locally for the attested import transport and does not persist the plaintext key. The current WaaS key origins are `.enclave` and `.imported`; Swift preserves an unrecognized future value as `.unknown(String)`.

Attestation failures throw `OMSWalletError` with code `.attestationVerificationFailed`.

<Warning>
  Development uses Nitro debug-mode attestation. Use only disposable test keys in Development. Staging and Production verify against SDK-pinned enclave measurements.
</Warning>

## Understand access objects

Keep these three objects distinct:

| Object          | Purpose                                                                                                                          |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Wallet session  | Local active-wallet state used by protected SDK operations.                                                                      |
| Wallet ID token | Short-lived token returned by OMS for your backend to verify the active wallet.                                                  |
| Access grant    | A direct `WalletCredential` or remote smart session that can make signed requests for the wallet until it expires or is revoked. |

The OIDC provider ID token used during authentication is a fourth object: it is app-owned identity proof consumed as auth input, not an OMS wallet token.

## Request a wallet ID token

Call `getIdToken()` only after a wallet is active. Send the returned string to a backend that verifies it using the OMS issuer and JWKS flow described in [backend wallet verification](/wallets/sdk/guides/backend-wallet-verification).

```swift theme={null}
let walletIdToken = try await omsWallet.wallet.getIdToken(
    ttlSeconds: 3_600,
    customClaims: [
        "role": .string("member")
    ]
)
```

Custom claims originate in the client. Your backend should not treat them as trusted authorization state unless it controls how they are assigned.

## List and revoke access grants

Use `listAccess()` to load all access-grant pages for account-management UI.

```swift theme={null}
let grants = try await omsWallet.wallet.listAccess()

for grant in grants {
    switch grant {
    case .direct(let credential):
        print(credential.credentialId, credential.isCaller)
    case .remote(let remote):
        print(remote.credential.credentialId, remote.sessionId)
        print(remote.metadata, remote.grants)
    }
}
```

Use the async sequence when your UI should process one server page at a time.

```swift theme={null}
for try await page in omsWallet.wallet.listAccessPages(pageSize: 25) {
    for grant in page.grants {
        print(grant.credential.credentialId, grant.credential.isCaller)
    }
}
```

You can also request one page with `listAccessPage(pageSize:cursor:type:)`. Revoke only a grant whose credential has `isCaller == false`:

```swift theme={null}
if let grant = grants.first(where: { !$0.credential.isCaller }) {
    switch grant {
    case .direct(let credential):
        try await omsWallet.wallet.revokeAccess(
            credentialId: credential.credentialId
        )
    case .remote(let remote):
        try await omsWallet.wallet.revokeAccess(
            credentialId: remote.credential.credentialId,
            sessionId: remote.sessionId
        )
    }
}
```

For a remote grant, `sessionId` is required and revokes exactly that session. Revocation cannot be undone. Revoking the caller invalidates the credential making the current request and prevents subsequent protected operations from that session. Keep caller revocation out of the normal access-management UI.

## Authorize remote access

Remote access grants are bounded EVM smart sessions. Inspect the remote credential and show its returned metadata to the wallet owner before requesting approval:

This example uses Polygon Amoy. Grant limits are raw EVM base-unit amounts. A native-transfer limit is cumulative, so `1000000000000000` wei authorizes up to `0.001 POL` across the session.

```swift theme={null}
import Foundation

let credentialId = "REMOTE_CREDENTIAL_ID"
let metadata = try await omsWallet.wallet.inspectRemoteCredential(
    credentialId: credentialId
)

// Render the returned metadata in your consent UI and continue only after approval.
print(metadata.appName, metadata.appUrl)

let session = try await omsWallet.wallet.authorizeRemoteAccess(
    credentialId: credentialId,
    network: .polygonAmoy,
    grants: [
        .nativeTransfer(
            to: "0x1111111111111111111111111111111111111111",
            // Cumulative 0.001 POL limit in wei.
            limit: "1000000000000000"
        )
    ],
    expiresAt: ISO8601DateFormatter().string(
        from: Date().addingTimeInterval(3_600)
    )
)

let details = try await omsWallet.wallet.getRemoteAccessSession(
    sessionId: session.sessionId
)
let usage = try await omsWallet.wallet.getRemoteAccessSessionUsage(
    sessionId: session.sessionId,
    network: .polygonAmoy
)
```

WaaS caps the requested session expiry at the remote credential's expiry. Backend credential registration and remote execution are outside the Swift SDK; implement them with the TypeScript SDK's [backend smart sessions guide](/wallets/sdk/guides/backend-smart-sessions).

## Sign out

```swift theme={null}
try omsWallet.wallet.signOut()
```

Signing out clears the completed session, Keychain credential, pending redirect state, and pending wallet selection for this SDK scope. It does not revoke other access credentials on the wallet.
