> For the complete documentation index, see [llms.txt](https://rc.sunbird.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://rc.sunbird.org/use/integrations/wallet-integration.md).

# Wallet Integration

{% hint style="info" %}
**Applies to** Sunbird RC v2.1.0 and later · **Service** `oid4vc-service` · **Protocols** OpenID4VCI 1.0, OpenID4VP 1.0/draft-23
{% endhint %}

From v2.1.0, Sunbird RC speaks wallet protocols directly — a conformant wallet can receive a credential from a Sunbird RC issuer and present it back to a Sunbird RC verifier with zero Sunbird-specific integration (no SDK, no issuer pre-registration, no proprietary API).

{% stepper %}
{% step %}

### Demo apps

Two thin reference UIs over the public `oid4vc-service` endpoints demonstrate both halves of the flow (issuer and verifier), with no privileged access beyond what a third party could build.
{% endstep %}

{% step %}

### Issuer flow

A schema must opt in with an `oid4vciConfig` block (`oid4vciEnabled: true`, plus `oid4vciFormats`) before it's visible to wallets. Discovery is a live lookup, so newly-opted-in schemas show up immediately — no cache to bust.
{% endstep %}

{% step %}

### Verifier flow

Pick or write a DCQL query in the verifier app → scan the presentation-request QR with the same wallet → consent to disclosure.

The verifier's status panel reports six checks: `holderSignature`, `nonce`, `credentialSignatures`, `holderBinding`, `revocation`, `dcql`. All six passing means the presenter controls the holder key, the credential is genuine and unrevoked, it's bound to that holder, and the disclosed claims satisfy the query exactly. Any failure shows which stage failed.
{% endstep %}
{% endstepper %}

For the curl-level setup and full API walkthrough, see `services/oid4vc-service/README.md` §5 (deployment) and §6 (manual testing) in the `sunbird-rc-core` repo.

## Wallet compatibility flags

Three flags exist purely to accommodate wallets at different points on the OpenID4VC standards timeline. None affect the credential itself, only the protocol envelope.

| Flag                             | Default | Effect                                                                                                                                                                                                                                            |
| -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `DRAFT13_COMPAT_MODE`            | `false` | Emit OpenID4VCI draft-13 shapes instead of final 1.0 (`credentials_supported` in metadata, `credentials` in the offer object, `user_pin_required` in the grant, retained `c_nonce` in the token response). Needed by the MOSIP Inji Wallet today. |
| `OID4VP_SIGN_REQUEST`            | `true`  | Sign the OpenID4VP request object as a JAR with a `did:`-prefixed `client_id`. Set `false` for unsigned plain JSON; overridable per-request via `{"signed": false}`.                                                                              |
| `OID4VP_LEGACY_CLIENT_ID_SCHEME` | `false` | Emit the pre-draft-22 shape — `client_id` is the bare `response_uri`, with `client_id_scheme: "redirect_uri"` as a separate field, unsigned. Needed by walt.id.                                                                                   |

**OpenID4VP request modes**

| Mode               | `client_id`                                    | Signing                                | Selected by                           |
| ------------------ | ---------------------------------------------- | -------------------------------------- | ------------------------------------- |
| signed *(default)* | `did:<VERIFIER_DID>`                           | JWS, `application/oauth-authz-req+jwt` | `OID4VP_SIGN_REQUEST` unset or `true` |
| unsigned           | `<response_uri>`                               | none, plain JSON                       | `{"signed": false}` per request       |
| legacy             | `<response_uri>` + separate `client_id_scheme` | none, plain JSON                       | `OID4VP_LEGACY_CLIENT_ID_SCHEME=true` |

{% hint style="info" %}
Per spec, the `redirect_uri` client-id scheme must never be signed — which is why choosing signed mode forces the `did:` prefix.
{% endhint %}

## What the wallet actually talks to

Issuer/verifier-side calls like `POST /oid4vc/offer` and `POST /vp/request` are never wallet-facing.

{% tabs %}
{% tab title="Issuance (OpenID4VCI)" %}

| Endpoint                                    | Purpose                                                     |
| ------------------------------------------- | ----------------------------------------------------------- |
| `GET /.well-known/openid-credential-issuer` | Discovery, live from `credential-schema`'s opted-in schemas |
| `GET /oid4vc/offer/:id`                     | Dereference the offer deep link                             |
| `POST /oid4vc/token`                        | `pre-authorized_code` grant → access token + `c_nonce`      |
| `POST /oid4vc/credential`                   | Proof-of-possession JWT → signed credential                 |
| {% endtab %}                                |                                                             |

{% tab title="Presentation (OpenID4VP)" %}

| Endpoint                            | Purpose                                      |
| ----------------------------------- | -------------------------------------------- |
| `GET /vp/request-object/:id`        | Fetch the request object (JWS or plain JSON) |
| `POST /vp/response` (`direct_post`) | Submit the `vp_token`                        |
| {% endtab %}                        |                                              |
| {% endtabs %}                       |                                              |

Deep links: `openid-credential-offer://...` (offers), `openid4vp://...` (presentation requests) — both delivered as `qr_data`. Holder binding accepts either a resolvable DID (`kid` in the PoP JWT) or an inline JWK/`did:jwk`, so wallets that don't publish DIDs still work. `oid4vc-service` itself holds no credential keys or storage — only short-lived session state and its own protocol signing key (which lives in Vault via `identity-service`).

For the complete env-var reference (TTL settings, Keycloak auth setup, per-schema issuer DID behavior), see `services/oid4vc-service/README.md` §4 in the `sunbird-rc-core` repo.

{% hint style="success" %}
**Verified wallet interop** — beyond the self-driven test harness (all four credential formats issue and verify end-to-end), `jwt_vc_json` and `vc+sd-jwt` issuance and presentation have been confirmed against independent third-party wallet implementations.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://rc.sunbird.org/use/integrations/wallet-integration.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
