Accept a W3C VC

Accept a W3C VC through the OID4VCI protocol

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

Accept a Credential over OID4VCI

This page will not go over how OpenID for Verifiable Credential issuance works, but we do encourage you to read the specification. The example code below shows a particular profile of OID4VCI, which you are not necessarily required to implement, but does illustrate that you can, i.e., generate a Proof of Possession with the SpruceKit Core Rust functionality.

// Import SpruceKit components
import com.spruceid.mobile.sdk.KeyManager
import com.spruceid.mobile.sdk.rs.AsyncHttpClient
import com.spruceid.mobile.sdk.rs.DidMethod
import com.spruceid.mobile.sdk.rs.HttpRequest
import com.spruceid.mobile.sdk.rs.HttpResponse
import com.spruceid.mobile.sdk.rs.Oid4vci
import com.spruceid.mobile.sdk.rs.Oid4vciExchangeOptions
import com.spruceid.mobile.sdk.rs.generatePopComplete
import com.spruceid.mobile.sdk.rs.generatePopPrepare
import io.ktor.client.HttpClient
import io.ktor.client.engine.cio.CIO


val client = HttpClient(CIO)

// Start an OID4VCI session with an async http client 
val oid4vciSession =
    Oid4vci.newWithAsyncClient(
        client =
        object : AsyncHttpClient {
            override suspend fun httpClient(
                request: HttpRequest
            ): HttpResponse {
                val res =
                    client.request(request.url) {
                        method = HttpMethod(request.method)
                        for ((k, v) in request.headers) {
                            headers[k] = v
                        }
                        setBody(request.body)
                    }

                return HttpResponse(
                    statusCode = res.status.value.toUShort(),
                    headers =
                    res.headers.toMap().mapValues {
                        it.value.joinToString()
                    },
                    body = res.readBytes()
                )
            }
        }
    )

    //initate the OID4VCI exchange
try {
    oid4vciSession.initiateWithOffer(
        credentialOffer = url,
        clientId = "skit-demo-wallet",
        redirectUrl = "https://spruceid.com"
    )

    val nonce = oid4vciSession.exchangeToken()

    val metadata = oid4vciSession.getMetadata()

    val keyManager = KeyManager()
    keyManager.generateSigningKey(id = "keyID")
    val jwk = keyManager.getJwk(id = "keyID")

    val signingInput =
        jwk?.let {
            generatePopPrepare(
                audience = metadata.issuer(),
                nonce = nonce,
                didMethod = DidMethod.JWK,
                publicJwk = jwk,
                durationInSecs = null
            )
        }

    // Create a signature over the payload
    val signature =
        signingInput?.let {
            keyManager.signPayload(
                id = "reference-app/default-signing",
                payload = signingInput
            )
        }

    // Finalize the Proof of Possession
    val pop =
        signingInput?.let {
            signature?.let {
                generatePopComplete(
                    signingInput = signingInput,
                    signature =
                    Base64.encodeToString(
                        signature,
                        Base64.URL_SAFE or
                                Base64.NO_PADDING or
                                Base64.NO_WRAP
                    )
                        .toByteArray()
                )
            }
        }
    // If needed, set the context, in the example we use VC Playground Json-LD contexts
    oid4vciSession.setContextMap(getVCPlaygroundOID4VCIContext(ctx = ctx))

    //Acquire the credentials from the OID4VCI exchange
    val credentials =
        pop?.let { oid4vciSession.exchangeCredential(
            proofsOfPossession = listOf(pop),
            options = Oid4vciExchangeOptions(true),
        ) }


    // Save the credentials 
    credentials?.forEach { cred ->
        cred.payload.toString(Charsets.UTF_8).let { credential = it }
    }
}

There is a lot of optionality in the OID4VCI protocol, and this code example only reflects one profile. Be sure to use the OID4VCI protocol in the way you want by studying the specification

Storing Credentials

There are multiple ways to store Credentials, but the most straightforward way is to use a CredentialPack. Even if you intend to store only a single credential in a CredentialPack, the component offers the most convenient API for interactions with other SpruceKit components.

// Store a Credential in a CredentialPack

val credentialPack = CredentialPack()
credentialPack.tryAddRawCredential(rawCredential)
try credentialPack.save(storageManager: StorageManager())

Last updated

Was this helpful?