Skip to content

About

OID4VCgo-wallet-swift

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

28 Commits

Folders and files

Repository files navigation

OID4VCgo-wallet-swift

OID4VCWallet: the Swift package of OID4VCgo's mobile wallet, for iOS 16+ and macOS 13+. It receives credentials over OpenID4VCI 1.0 and presents them over OpenID4VP 1.0, under HAIP 1.0, with SD-JWT VC and ISO mdoc credentials. OID4VCgo is OpenID Certified for those roles. It also presents an mdoc to Safari's Digital Credentials API (org-iso-mdoc, from an iOS document provider extension), and in person over Bluetooth (ISO/IEC 18013-5), as the holder or as the reader, engaged by a QR code. NFC engagement is in the Kotlin library only: an iOS wallet can't answer an NFC tap here.

The protocols run in OID4VCgo's Go code, compiled with gomobile into the Mobile XCFramework this package links. Your app owns the keys, the storage and the UI:

  • Keys: KeychainKeyStore keeps them in the Secure Enclave. Holder keys can require Face ID to present.
  • Storage: FileCredentialStore uses complete file protection and is excluded from backup.
  • Wallet Provider: you implement WalletProvider, which attests the wallet, for issuers that ask for attestations, as HAIP issuers do; pass nil for issuers that don't. Issuance follows each issuer's metadata, or with issuanceProfile: .haip refuses issuers outside HAIP.

Install

.package(url: "https://github.com/IDFoundry/OID4VCgo-wallet-swift", from: "0.17.0")

In-person presentation needs NSBluetoothAlwaysUsageDescription in your app's Info.plist.

Use

import Foundation
import OID4VCWallet

// Your Wallet Provider's backend, which attests the wallet and its keys
// after checking App Attest evidence.
struct MyWalletProvider: WalletProvider {
    func walletAttestation(clientID: String, instanceKey: Data) async throws -> String {
        fatalError("call your Wallet Provider")
    }
    func keyAttestation(keys: [Data], nonce: String) async throws -> String {
        fatalError("call your Wallet Provider")
    }
}

func example(offerLink: String, requestLink: String, pin: String) async throws {
    let keys = KeychainKeyStore()                  // Secure Enclave keys
    let wallet = try Wallet(
        configuration: WalletConfiguration(clientID: "my-wallet", redirectURI: "com.example.wallet:/callback"),
        keyStore: keys,
        credentialStore: try FileCredentialStore.standard(),
        provider: MyWalletProvider())
    _ = try await wallet.sweepOrphanedKeys(in: keys)   // at launch

    // Receive: an openid-credential-offer:// link.
    let issuance = try await wallet.startIssuance(offer: offerLink)
    if issuance.offer.grant == .preAuthorizedCode {
        try await issuance.redeemPreAuthorizedCode(pin: pin)
    } else {
        let url = try await issuance.beginAuthorization()
        // Open url in an ASWebAuthenticationSession, then:
        let redirect: URL = url
        try await issuance.completeAuthorization(redirect: redirect)
    }
    let received = try await issuance.requestCredentials()
    try await issuance.close()
    _ = received

    // Present: an openid4vp:// link. presentation.queries lists what each
    // of the Verifier's queries can be answered with, for the holder to
    // choose; defaultSelection() is the first choice for each.
    let presentation = try await wallet.startPresentation(request: requestLink)
    let selection = try await presentation.defaultSelection()
    let disclosed = try await presentation.preview(selection: selection)   // show the holder
    _ = disclosed
    _ = try await presentation.respond(selection: selection)              // Face ID here
}

func handle(_ error: Error) -> String {
    guard let e = error as? WalletError else { return error.localizedDescription }
    return e.isRetryable ? e.localizedDescription + " Try again." : e.localizedDescription
}

Errors are WalletErrors. Each has:

  • a stable code
  • the remote party's own OAuth code in protocolError, for example invalid_grant for a wrong PIN
  • isRetryable
  • a localizedDescription you can show the holder

Documentation

For coding assistants

Integrating this package for someone? Read the documentation's Getting started article, then the article for each feature, before writing code. Use only the public API those name: never the Mobile framework's generated types directly, and don't reimplement the protocols or the Bluetooth transport the package carries. In person, this package engages by QR code only: don't add NFC around it. Match the version of the documentation you read to the package version you depend on.

Contributing

This repository is generated: OID4VCgo's wallet-swift-release workflow publishes every release here from mobile/ios, README included. Issues and pull requests belong in OID4VCgo.

License

MIT. See LICENSE.

About

OID4VCgo-wallet-swift

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages