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