Verify a W3C VC

Verify a W3C Verifiable Credential with SpruceKit Mobile SDK

This guide is for iOS applications. Building for Android? Look here: Verify a W3C VC

1. Verify an embedded credential

In this flow, the Verifier will scan a QR-code presented by the Wallet. The QR-Code will directly contain the verifiable presentation encoded as a JWT. As such, the Verifier has to engage the QRCodeScanner component.


import SwiftUI
import SpruceIDMobileSdkRs

// Here is an example View to scan and try verify a W3C VC JWT VP
struct VerifyVCView: View {
    
    @State var success: Bool?
    
    @Binding var path: NavigationPath
    
    var body: some View {
        if success == nil {
            // Engage the SpruceKit Mobile SDK ScanningComponent
            QRCodeScanner(
                        title: "Scan QR Code",
                        subtitle: "Looking...",
                        onRead: onRead: { code in
                        Task {
                            do {
                                try await verifyJwtVp(jwtVp: code)
                                success = true
                            } catch {
                                success = false
                                print(error)
                            }
                        }
                    },
                        onCancel: onCancel,
                        // example font values
                        titleFont: .customFont(font: .inter, style: .bold, size: .h0),
                        subtitleFont: .customFont(font: .inter, style: .bold, size: .h4),
                        cancelButtonFont: .customFont(font: .inter, style: .medium, size: .h3),
                        readerColor: .white
                    )
            )
        } else {
            // Plug in your Verifier results view
        }
        
    }
}

Verify VCs

Do you know what kind of credential format you are verifying?

import SpruceIDMobileSdkRs

// Verify a W3C VC JWT-VP 
_ = try await VerifyJwtVp(jwtVp: vp)

// Verify a W3C VC Barcode
_ = try await VerifyPdf417Barcode(payload: payload)

// Verify a W3C VC Barcode against a Machine Readable Zone
_ = try await VerifyVcbQrcodeAgainstMrz(mrzPayload: mrz, qrPayload: vcb)

The steps as explained above allow you to execute a verification offline by scanning a credential embedded in the QR code. You can also verify online with OID4VP, using the DelegatedVerifer component. The DelegatedVerifier allows your Mobile App Verifier to rely on your own web service to handle a PresentationRequest, and receive and validate the response. The Mobile App will then only receive an outcome back from your web service.

2. Delegated Verifier

In the delegated flow, the Verifier generates a QR-code to advertise the PresentationRequest to the Wallet. To use the DelegateVerifier, you will need a back end service that executes the OID4VP transaction. From your mobile application, you will only advertise the request and poll the status. To manage the UI during the verification, you will have to manage the DelegatedVerifierStatus. You can monitor that state, like in the example below, and manage your UI based on the state.

// Define your DelegatedVerifier View

struct VerifyDelegatedOid4vpView: View {
    @Binding var path: NavigationPath
    var verificationId: Int64
    var verificationMethod: VerificationMethod?
    var url: URL?
    var baseUrl: String
    
    @State var step = VerifyDelegatedOid4vpViewSteps.loadingQrCode
    @State var status = DelegatedVerifierStatus.initiated
    @State var loading: String? = nil
    @State var errorTitle: String? = nil
    @State var errorDescription: String? = nil
    
    @State var verifier: DelegatedVerifier? = nil
    @State var authQuery: String? = nil
    @State var uri: String? = nil
    @State var presentation: String? = nil

    
    init(path: Binding<NavigationPath>, verificationId: Int64) {
        self._path = path
        self.verificationId = verificationId
        do {
            // Verification method from db
            verificationMethod = try VerificationMethodDataStore
                .shared
                .getVerificationMethod(rowId: verificationId)
                .unwrap()
            
            // Verification method base url
            url = URL(string: verificationMethod!.url)
            
            let unwrappedUrl = try url.unwrap()
                
            baseUrl = unwrappedUrl
                .absoluteString
                .replacingOccurrences(of: unwrappedUrl.path(), with: "")
        } catch {
            self.errorTitle = "Failed Initializing"
            self.errorDescription = error.localizedDescription
            self.verificationMethod = nil
            self.url = URL(string: "")
            self.baseUrl = ""
        }
    }
    
    func monitorStatus(status: DelegatedVerifierStatus) async {
        do {
            let res = try await verifier?.pollVerificationStatus(url: "\(uri.unwrap())?status=\(status)")
            
            if let newStatus = res?.status {
                switch newStatus {
                    // Manage your DelegatedVerifierStatus here
                }
            } else {
                // if can't find res.status, call monitorStatus
                // with the same parameters
                await monitorStatus(status: status)
            }
        } catch {
            errorTitle = "Error Verifying Credential"
            errorDescription = error.localizedDescription
        }
    }
    
    func initiateVerification() {
        Task {
            do {
                let unwrappedUrl = try url.unwrap()

                // Delegated Verifier
                verifier = try await DelegatedVerifier.newClient(baseUrl: baseUrl)
                
                // Get initial parameters to delegate verification
                let delegatedVerificationUrl = "\(unwrappedUrl.path())?\(unwrappedUrl.query() ?? "")"
                let delegatedInitializationResponse = try await verifier
                    .unwrap()
                    .requestDelegatedVerification(url: delegatedVerificationUrl)
                    
                authQuery = "openid4vp://?\(delegatedInitializationResponse.authQuery)"
                
                uri = delegatedInitializationResponse.uri
                
                // Display QR Code
                step = VerifyDelegatedOid4vpViewSteps.presentingQrCode
                
                // Call method to start monitoring status
                await monitorStatus(status: status)
            } catch {
                errorTitle = "Failed getting QR Code"
                errorDescription = error.localizedDescription
            }
        }
    }
    
    func onBack() {
        while !path.isEmpty {
            path.removeLast()
        }
    }
    
    var body: some View {
        ZStack {
            if errorTitle != nil && errorDescription != nil {
                ErrorView(
                    errorTitle: errorTitle!,
                    errorDetails: errorDescription!,
                    onClose: onBack
                )
            } else {
                switch step {
                    //  Manage your VerifyDelegatedOid4vpViewSteps here
                }
            }
        }
        .navigationBarBackButtonHidden(true)
        .onAppear(perform: {
            initiateVerification()
        })
    }
}

To implement your OID4VP Server, take a look at our open source OID4VP library

Last updated

Was this helpful?