OEM Cookbook · self-issue from OIDC identity

Self-issue a credential from the signed-in user.

When the credential you want to hand your OEM user's wallet is derived from their own OIDC profile on CodeB (e.g. an internal employee VC, an in-app membership credential), skip the full OID4VCI ceremony and use the two-endpoint self-issue path: B1 available-attributes to see what the user is eligible for, and B2 issue-in-session to mint. The credential is key-bound to a fresh on-device wallet key. European Digital Identity Wallet compatible — the output is a standard dc+sd-jwt that any conformant verifier can consume.

1 Sign in via OIDC (standard PKCE)

Same as any OIDC integration. Obtain a Bearer access token for your CodeB tenant.

2 Enumerate eligible credentials (B1)

GET /vci.ashx?available-attributes=1
Authorization: Bearer <access token>

Response:

{
  "subject": "<OIDC sub>",
  "credentials": [
    { "vct": "urn:eudi:pid:1", "ready": true,  "missing": [], "display": "Personal Identification Data" },
    { "vct": "https://aloaha.com/vc/employee_id",
                                "ready": false, "missing": ["employee_number"], "display": "Employee ID" }
  ]
}

Show only ready: true as import candidates. Use missing to explain the block to the user.

3 Generate the wallet keypair

Same pattern as the import cookbook §4. On iOS use SecureEnclave.P256.Signing.PrivateKey; on Android KeyPairGenerator.getInstance("EC","AndroidKeyStore"). Export the public JWK for the cnf.jwk field.

iOS Swift

let credentialId = UUID().uuidString
let key = try createWalletKey(credentialId: credentialId)
let pubJwk = publicJwk(key)

Android Kotlin

val credentialId = UUID.randomUUID().toString()
val pair = createWalletKey(credentialId)
val pubJwk = publicJwk(pair.public as ECPublicKey)

4 Mint the credential (B2)

POST /vci.ashx?issue-in-session=1
Authorization: Bearer <access token>
Content-Type: application/json

{
  "vct": "urn:eudi:pid:1",
  "cnf": { "jwk": { "kty":"EC", "crv":"P-256", "x":"...", "y":"..." } }
}

Response:

{
  "credentials": [ { "credential": "<SD-JWT with disclosures>" } ],
  "credential":  "<SD-JWT with disclosures>",
  "vct":         "urn:eudi:pid:1",
  "format":      "dc+sd-jwt"
}

credentials is an array of objects, each carrying a credential field (per OID4VCI 1.0 FINAL §8.3). The scalar credential is emitted alongside for backwards compatibility. The disclosures are already appended to the SD-JWT string, separated by ~.

5 Persist

Store { id: credentialId, format: "dc+sd-jwt", vct, credential, keyHandle } in your secure store. See the import cookbook §7 for the pattern.

? When to use self-issue vs full OID4VCI

  • Self-issue (this cookbook) — when CodeB is BOTH the identity provider and the credential issuer for the user. Faster (two calls), no browser hop.
  • Full OID4VCI (import cookbook) — when an external issuer mints the credential, OR when you need to test/support the standardised flow, OR when regulation requires an explicit consent step (browser hop = user visibly authorised issuance).

Previous: vault sync API reference OEM landing