Verifiably β Backend Integration Guide
This prototype is fully functional against an in-memory mock layer. To connect a real backend, you do not touch the UI code. You reimplement the BACKEND object inside vc-prototype.html.
Where to look in the code
Open vc-prototype.html and search for these three markers, in order:
-
const MOCK = {β every piece of fake data (DPG catalogs, schemas, subject values, issuer identities, offer URIs, verification outcomes). You can delete this entire object once the backend is live, but it's useful to keep around as a fixture for local development. -
const BACKEND = {β the ten async adapter functions. Their names, argument shapes, and return shapes are stable. Replace each body with afetch(...)call to your real endpoint. The UI calls these viaawait BACKEND.foo(...)and doesn't care where the data comes from. -
const DEBUG_SHOW_MOCK_MARKERS = falseβ flip totrueduring integration to see small[mock]pills on every UI surface still rendering from the mock layer. When you've wired all adapters, none of the pills should appear.
Adapter β endpoint mapping per DPG
Each BACKEND.* function has a // TODO: comment naming the concrete endpoint. The table below expands those into copy-ready targets. Base URLs shown as env-var placeholders β substitute your deployed DPG URLs.
| Adapter | walt.id Community Stack v0.18.2 | Inji Certify v0.14.0 | Inji Web Wallet v0.16.0 | Inji Verify v0.16.0 |
|---|---|---|---|---|
listIssuerDpgs |
N/A (app-level registry) | N/A | β | β |
listHolderDpgs |
β | β | N/A (app-level registry) | β |
listVerifierDpgs |
β | β | β | N/A (app-level registry) |
listSchemas(issuerDpg) |
GET {ISSUER_API}/credentialConfigurations |
GET {CERTIFY_API}/v1/certify/credential-configuration |
β | β |
prefillSubjectFields(schema) |
N/A (operator types into form) | MOSIP Identity Plugin: GET {CERTIFY_API}/v1/certify/issuance/data-provider/{plugin}?key={uin} |
β | β |
issueToWallet({issuerDpg, schema, subjectData}) |
POST {ISSUER_API}/openid4vc/jwt/issue (or /sdjwt/issue, /mdoc/issue depending on format) β returns offer URI |
POST {CERTIFY_API}/v1/certify/issuance/credential-offer β returns pre-authorized code + offer URI |
β | β |
issueAsPdf({issuerDpg, schema, subjectData}) |
Not supported at v0.18.2 β must build on top of the issuer library | Relies on Claim 169 QR Code spec (pixelpass lib) + MOSIP Identity Plugin; POST {CERTIFY_API}/v1/certify/issuance/credential with QR-embed flag |
β | β |
issueBulk({issuerDpg, schema, csvRows}) |
POST {ISSUER_API}/openid4vc/batchIssue |
POST {CERTIFY_API}/v1/certify/issuance/bulk (check v0.14.0 docs for exact shape) |
β | β |
parseOffer(offerUri) |
POST {WALLET_API}/wallet/{walletId}/exchange/resolveCredentialOffer |
β | Redirect flow β not called from this app | β |
claimCredential(offer) |
POST {WALLET_API}/wallet/{walletId}/exchange/useOfferRequest |
β | Redirect flow | β |
requestPresentation({verifierDpg, templateKey}) |
POST {VERIFIER_API}/openid4vc/verify β returns request URI + state |
β | β | Redirect flow |
fetchPresentationResult({state}) |
GET {VERIFIER_API}/openid4vc/session/{state} or register a statusCallbackUri on the verify call |
β | β | Redirect flow |
verifyDirect({verifierDpg, method, credentialData}) |
POST {VERIFIER_API}/openid4vc/verify/credential |
β | β | For W3C VCDM: POST {VERIFY_API}/v1/verify/vc-verification. For SD-JWT VC (new in v0.16.0): POST {VERIFY_API}/v1/verify/vc-submission then GET /v1/verify/vp-result/{transactionId} |
Authentication
OIDC providers are configured in two layered files plus a runtime admin UI:
config/auth-providers.system.jsonβ bootstrap providers written bydeploy.sh(defaults: Keycloak + WSO2 IS). Re-run-safe;./deploy.sh run alloverwrites it. SetVERIFIABLY_NO_DEFAULT_IDPS=1to skip the demo defaults entirely β the UI then shows a first-run form on/auththat lets the operator register their own provider before signing in.config/auth-providers.user.jsonβ admin-UI-managed providers. Persisted across./deploy.shruns. Created on first POST to/admin/auth-providers(or migrated automatically from any pre-existingauth-providers.docker.json.bak). User entries override system entries with the sameid.VERIFIABLY_OIDC_PROVIDERSenv (JSON array) β when set, replaces both files for that boot. Highest precedence; useful for IaC / Helm / Compose deploys.
Per-field env overrides (VERIFIABLY_OIDC_<ID>_{ISSUER_URL,CLIENT_ID,CLIENT_SECRET,DISPLAY_NAME,KIND,SCOPES,INSECURE_SKIP_VERIFY}) apply on top of the file/env-JSON merge.
Surface control via VERIFIABLY_AUTH_ADMIN. Persistence is independent of this flag β it works in every mode.
| Value | Admin page | /auth "+ Add" form |
Effect |
|---|---|---|---|
rw (default) |
Visible (login + list + delete) | Visible | Full surface; admin curates AND users can self-add. |
ro |
Visible (login + list + delete) | Hidden | Admin curates a fixed list; end users can only pick from existing tiles. |
off |
404 (no nav link, no admin login) | Hidden | No UI for provider management at all. Providers come only from VERIFIABLY_OIDC_PROVIDERS / auth-providers.system.json / per-field env overrides. |
Fresh-install (registry empty) bypasses the ro and off add-form lockdown so a single env flag can't permanently shut out bootstrapping.
Admins can delete rows of any source (user, system, env). System / env rows reappear on the next ./deploy.sh run all / container restart β that's the desired behaviour during admin iteration; for permanent removal, edit the source (deploy.sh's auth_providers_for / the env var) and restart.
The UI already has a Keycloak / WSO2 provider selection on the auth screen. Once authenticated, whatever session token your IDP returns should be attached to every BACKEND.* call. A clean way to do this:
const BACKEND = {
_fetch(url, opts = {}) {
return fetch(url, {
...opts,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${STATE.authToken}`,
...(opts.headers || {})
}
}).then(r => r.ok ? r.json() : Promise.reject(r));
},
async listSchemas(issuerDpg) {
const base = ISSUER_BASE_URLS[issuerDpg];
return this._fetch(`${base}/credentialConfigurations`);
},
// ...
};
Store the token in STATE.authToken from completeAuth() (currently stubs auth β swap with a real IDP redirect + token exchange).
Format note β return shapes
The UI expects specific shapes. If your backend returns something different, adapt it inside the BACKEND adapter rather than changing the UI:
listSchemasreturns an array of{ id, name, std, dpgs, desc }.issueToWalletreturns{ offerUri, offerId, flow, expiresIn }.issueAsPdfreturns{ issuerName, issuerDid, payloadSizeKb, fields }.issueBulkreturns{ accepted, rejected, errors: [{ row, reason }] }.parseOfferreturns{ title, issuer, type, fields }wherefieldsis a flat keyβvalue map.claimCredentialreturns the claimed credential withstatus: 'accepted'added.requestPresentationreturns{ requestUri, state, template: {...} }.fetchPresentationResultandverifyDirectreturn{ valid, method, format, issuer, subject, requested?, issued, checkedRevocation }.
If your verifier supports richer result metadata (signature suite, trust chain, revocation list source, etc.), add fields to the return shape β renderVerifyResult(opts) gracefully ignores unknown fields, and you can extend it when you need to surface them.
Cross-version compatibility warning
The four DPG versions used in this prototype (walt.id v0.18.2, Certify v0.14.0, Inji Web v0.16.0, Inji Verify v0.16.0) are not a tested-compatible matrix β each vendor publishes its own compatibility table. When deploying:
- Inji Web Wallet v0.16.0 declares compatibility with Inji Certify v0.13.1 and Inji Verify v0.17.0.
- Inji Verify v0.16.0 declares compatibility with Inji Web v0.14.0 and Inji Wallet v0.20.0.
- walt.id Community Stack is versioned as a unit; all three (issuer, wallet, verifier) ship together at v0.18.2.
Pick one vendor's matrix and align. Mixing versions is how interop bugs happen.
Known issues to guard around
- INJIVER-1131 (Inji Verify v0.16.0 cross-device flow): presentation is reported valid even when a wrong VC is submitted. Implement credential-type validation in your Relying Party code until fixed upstream.
- INJICERT mDoc (v0.14.0): issuance is mock-only per the GitHub README. Don't ship mDoc credentials from Certify in production.
- walt.id wallet OID4VP v1.0 (through v0.18.2): not yet fully supported in the wallet/demo apps. Older OID4VP (Presentation Exchange) works.
Swap checklist
Once the backend is live, verify:
-
DEBUG_SHOW_MOCK_MARKERS = trueshows no[mock]pills during a full end-to-end flow - DPG capability cards still reflect your deployed versions (edit
MOCK.issuerDpgsetc., or replace with alistIssuerDpgsbackend call) -
parseOffercorrectly handles bothcredential_offer=...(inline) andcredential_offer_uri=...(by-reference) forms - Auth token refresh is handled for long-lived sessions
- Errors from
BACKENDcalls surface as user-friendly toasts (currently most failure paths throw silently)