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:
KeychainKeyStorekeeps them in the Secure Enclave. Holder keys can require Face ID to present. - Storage:
FileCredentialStoreuses 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; passnilfor issuers that don't. Issuance follows each issuer's metadata, or withissuanceProfile: .haiprefuses issuers outside HAIP.
.package(url: "https://github.com/IDFoundry/OID4VCgo-wallet-swift", from: "0.17.0")In-person presentation needs NSBluetoothAlwaysUsageDescription in your
app's Info.plist.
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 exampleinvalid_grantfor a wrong PIN isRetryable- a
localizedDescriptionyou can show the holder
- The OID4VCWallet documentation: getting started, an article for each task, and the API reference.
- What's new: each release's notes.
- OID4VCgo's
mobile/: what each platform supports, in-person presentation, the Go side and its ABI. mobile/MOBILE.md: the design.- The demo wallet app: a complete app on this package, including the document provider.
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.
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.
MIT. See LICENSE.