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 . 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 }
}
}
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())
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