eu-digital-identity-wallet / eudi-lib-ios-wallet-kit

This repository contains the EUDI Wallet Kit library for iOS. The library is a part of the EUDI Wallet Reference Implementation project.
https://eu-digital-identity-wallet.github.io/eudi-lib-ios-wallet-kit/
Apache License 2.0
11 stars 14 forks source link
18013-5 openid4vp walllet

EUDI Wallet Kit library for iOS

:heavy_exclamation_mark: Important! Before you proceed, please read the EUDI Wallet Reference Implementation project description


EUDI ISO iOS Wallet Kit library

License Swift Lines of Code Duplicated Lines (%) Reliability Rating Vulnerabilities

Overview

This repository contains the EUDI Wallet Kit library for iOS. The library is a part of the EUDI Wallet Reference Implementation project.

This library acts as a coordinator by orchestrating the various components that are required to implement the EUDI Wallet functionality. On top of that, it provides a simplified API that can be used by the application to implement the EUDI Wallet functionality.

graph TD;
    A[eudi-lib-ios-wallet-kit]
    B[eudi-lib-ios-wallet-storage] -->  |Wallet Storage|A 
    C[eudi-lib-ios-iso18013-data-transfer] --> |Transfer Manager|A 
    D[eudi-lib-ios-openid4vci-swift] --> |OpenId4Vci Manager|A 
    E[eudi-lib-ios-siop-openid4vp-swift] --> |OpenId4Vp Manager|A 
    F[eudi-lib-ios-iso18013-security] --> |Mdoc Security|C 
    G[eudi-lib-ios-iso18013-data-model] --> |Mdoc Data Model|C 
    H[eudi-lib-ios-presentation-exchange-swift] --> E 

The library provides the following functionality:

The library is written in Swift and is compatible with iOS 14 or higher. It is distributed as a Swift package and can be included in any iOS project.

It is based on the following specifications:

Disclaimer

The released software is a initial development release version:

Installation

To use EUDI Wallet Kit, add the following dependency to your Package.swift:

dependencies: [
    .package(url: "https://github.com/eu-digital-identity-wallet/eudi-lib-ios-wallet-kit.git", .upToNextMajor(from: "0.6.6"))
]

Then add the Eudi Wallet package to your target's dependencies:

dependencies: [
    .product(name: "EudiWalletKit", package: "eudi-lib-ios-wallet-kit"),
]

Reference

Detailed documentation is provided in the DocC documentation here

Initialization

The EudiWallet class provides a unified API for the two user attestation presentation flows. It is initialized with a document storage manager instance. For SwiftUI apps, the wallet instance can be added as an environmentObject to be accessible from all views. A KeyChain implementation of document storage is available.

let wallet = EudiWallet.standard
wallet.userAuthenticationRequired = true
wallet.trustedReaderCertificates = [...] // array of der certificate data
wallet.openId4VpVerifierApiUri = "https:// ... verifier api uri ..."
wallet.verifierApiUri = configLogic.verifierConfig.apiUri
wallet.verifierLegalName = configLogic.verifierConfig.legalName
wallet.openID4VciIssuerUrl = configLogic.vciConfig.issuerUrl
wallet.openID4VciClientId = configLogic.vciConfig.clientId
wallet.openID4VciRedirectUri = configLogic.vciConfig.redirectUri
wallet.loadAllDocuments()

Manage documents

The EudiWallet class provides a set of methods to work with documents.

Loading documents

The loadDocuments method returns documents with a specific status from storage.

The following example shows how to retrieve issued documents:

 public func loadDocuments() async throws {
    let documents = try await wallet.loadDocuments(status: .issued)
  }

To retrieve documents of all statuses use the loadAllDocuments method.

let documents = try await wallet.loadAllDocuments()

The loadDocument(id:status:) method returns a document with a given id and status.

The following example shows how to retrieve a document:

let document = try await wallet.loadDocument(id: documentId, status: .issued)

Storage manager

The read-only property storage is an instance of a StorageManager class. Currently the keychain implementation is used. It provides document management functionality using the iOS KeyChain.

The storage model provides the following models for the supported well-known document types:

DocType Model
eu.europa.ec.eudiw.pid.1 EuPidModel
org.iso.18013.5.1.mDL IsoMdlModel

Since the issued mDoc documents retrieved expose only basic metadata and the raw data, they must be decoded to the corresponding CBOR models. The library provides the StorageManager\toMdocModel function to decode document raw CBOR data to strongly-typed models conforming to MdocDecodable protocol.

The loading functions automatically update the StorageManager members. The decoded issued documents are available in the mdocModels property. The deferred and pending documents are available in the StorageManager\deferredDocuments and StorageManager\pendingDocuments properties respectively.

For other document types the GenericMdocModel is provided.

Deleting a document

The deleteDocument(id:) method that deletes a document with the given id.

The following example shows how to delete a document:

try await wallet.deleteDocument(id: documentId)

Issue document using OpenID4VCI

The library provides the functionality to issue documents using OpenID4VCI.

To issue a document using this functionality, EudiWallet must be property initialized. If userAuthenticationRequired is true, user authentication is required. The authentication prompt message has localisation key "issue_document". After issuing a document, the document data and corresponding private key are stored in the wallet storage.

Issue document by docType

When the document docType to be issued use the issueDocument(docType:format:) method.

Important Notes:

The following example shows how to issue an EUDI Personal ID document using OpenID4VCI:

wallet.openID4VciIssuerUrl = "https://eudi.netcompany-intrasoft.com/pid-issuer" 
wallet.openID4VciClientId = "wallet-dev"
wallet.openID4VciRedirectUri = "eudi-openid4ci://authorize/" 
do {
  let doc = try await userWallet.issueDocument(docType: EuPidModel.euPidDocType, format: .cbor)
  // document has been added to wallet storage, you can display it
}
catch {
  // display error
}

Resolving Credential offer

The library provides the resolveOfferUrlDocTypes(uriOffer:) method that resolves the credential offer URI. The method returns the resolved OfferedIssuanceModel object that contains the offer's data (offered document types, issuer name and transaction code specification for pre-authorized flow). The offer's data can be displayed to the user.

The following example shows how to resolve a credential offer:

 func resolveOfferUrlDocTypes(uriOffer: String) async throws -> OfferedIssuanceModel {
    return try await wallet.resolveOfferUrlDocTypes(uriOffer: uriOffer)
  }

After user acceptance of the offer, the selected documents can be issued using the issueDocumentsByOfferUrl(offerUri:docTypes:txCodeValue:format:) method. The txCodeValue parameter is not used in the case of the authorization code flow. The following example shows how to issue documents by offer URL:

 let documents = try await walletController.issueDocumentsByOfferUrl(offerUri: uri,  docTypes: docOffers, format: .cbor, txCodeValue: txCodeValue )

Authorization code flow

For the authorization code flow to work, the redirect URI must be specified specified by setting the the openID4VciRedirectUri property. The user is redirected in an authorization web view to the issuer's authorization endpoint. After the user authenticates and authorizes the request, the issuer redirects the user back to the application with an authorization code. The library exchanges the authorization code for an access token and issues the document.

Pre-Authorization code flow

When Issuer supports the pre-authorization code flow, the resolved offer will also contain the corresponding information. Specifically, the txCodeSpec field in the OfferedIssuanceModel object will contain:

From the user's perspective, the application must provide a way to input the transaction code.

After user acceptance of the offer, the selected documents can be issued using the issueDocumentsByOfferUrl(offerUri:docTypes:txCodeValue:format:) method. When the transaction code is provided, the issuance process can be resumed by calling the above-mentioned method and passing the transaction code in the txCodeValue parameter.

Dynamic issuance

Wallet kit supports the Dynamic PID based issuance

After calling issueDocument(docType:format:) or issueDocumentsByOfferUrl(offerUri:docTypes:txCodeValue:format:) the wallet application need to check if the doc is pending and has a authorizePresentationUrl property. If the property is present, the application should perform the OpenID4VP presentation using the presentation URL. On success, the resumePendingIssuance(pendingDoc:, webUrl:) method should be called with the authorization URL provided by the server.

if let urlString = newDocs.last?.authorizePresentationUrl { 
    // perform openid4vp presentation using the urlString 
    // on success call resumePendingIssuance using the authorization url  

Presentation Service

The presentation service protocol abstracts the presentation flow. The BlePresentationService and OpenId4VpService classes implement the proximity and remote presentation flows respectively. The PresentationSession class is used to wrap the presentation service and provide @Published properties for SwiftUI screens. The following example code demonstrates the initialization of a SwiftUI view with a new presentation session of a selected flow type.

let session = eudiWallet.beginPresentation(flow: flow)
// pass the session to a SwiftUI view
ShareView(presentationSession: session)

On view appearance the attestations are presented with the receiveRequest method. For the BLE (proximity) case the deviceEngagement property is populated with the QR code to be displayed on the holder device.

.task {
     if presentationSession.flow.isProximity { await presentationSession.startQrEngagement() }
     _ = await presentationSession.receiveRequest()
}

After the request is received the presentationSession.disclosedDocuments contains the requested attested items. The selected state of the items can be modified via UI binding. Finally, the response is sent with the following code:

// Send the disclosed document items after biometric authentication (FaceID or TouchID)
// if the user cancels biometric authentication, onCancel method is called
 await presentationSession.sendResponse(userAccepted: true,
  itemsToSend: presentationSession.disclosedDocuments.items, onCancel: { dismiss() }, onSuccess: {
            if let url = $0 { 
        // handle URL
       }
        })

Logging

The SwiftLog library is used for logging. The library provides a default logger that logs to the console. The main app configures logging outputs such as file logging. To use the logger create a logger instance with the desired label. The logger can be used to log messages with different log levels.

import Logging
// Create a logger with a label
let logger = Logger(label: "com.example.BestExampleApp.main")
// log an info message
logger.info("Hello World!")

Reference

Detailed documentation is provided in the DocC documentation here

Dependencies

The detailed functionality of the wallet kit is implemented in the following Swift Packages: MdocDataModel18013, MdocSecurity18013, MdocDataTransfer18013 and SiopOpenID4VP OpenID4VCI

Reference application

A reference application that demonstrates the usage of this library is App Wallet UI.

How to contribute

We welcome contributions to this project. To ensure that the process is smooth for everyone involved, follow the guidelines found in CONTRIBUTING.md.

License details

Copyright (c) 2023 European Commission

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.