ABDM Webhook Gateway Token Settings
ABDM gateway callbacks (/integration/abdm/webhooks/* and /integration/abdm/api/v3/*) carry an
RS256 bearer token from the gateway. Integration::Abdm::WebhookAuthenticator verifies its signature,
and can also check its issuer, its audience and a claim that names the HIP or HIU. Those last three
need values taken from a real sandbox gateway token. The repo has no captured token, so they are not
set yet. This page lists them so the ABDM integration owner can fill them in (#2416).
What is enforced today
| Check | Where | Without the setting |
|---|---|---|
RS256 signature against the gateway JWKS (jwks_url) | authenticator | always on |
Issuer (iss) | authenticator | off. Production refuses to boot; staging boots and logs a warning |
Audience (aud) | authenticator | off. When set, a token with no aud still passes |
Token service-id claim equals X-HIP-ID / X-HIU-ID | controller | checked only when the token has the claim |
| Token must have the service-id claim | authenticator | off (abdm_require_service_id_claim is false) |
| Body HIP/HIU id equals the header | controller | always on, where the body has the field (table below) |
While abdm_require_service_id_claim is off, a token with no service-id claim still takes its tenant
from the unsigned X-HIP-ID / X-HIU-ID header. The body check narrows that, but the body is not
signed either. Turning the setting on is what closes #2416.
Settings that need a captured token
Each setting is read from the env var first, then from Rails credentials (top-level key, not nested).
| Setting | Env var | Credential key | What to take from the token |
|---|---|---|---|
| Expected issuer | ABDM_JWT_EXPECTED_ISS | abdm_jwt_expected_iss | The exact iss value. Sandbox and production differ, so set it per environment |
| Expected audience | ABDM_JWT_EXPECTED_AUD | abdm_jwt_expected_aud | The aud value, if the token has one. Leave unset if it has none |
| Service-id claim path | ABDM_SERVICE_ID_CLAIM | abdm_service_id_claim_path | The dotted path of the claim whose value is our HIP/HIU id (for example a.b). Defaults to service_id, which is a guess, not a known ABDM claim |
| Require the claim | ABDM_REQUIRE_SERVICE_ID_CLAIM | abdm_require_service_id_claim | Set true only after the path above is confirmed on both a HIP and an HIU callback |
How to capture a token
- On staging, let a sandbox callback arrive (for example a discover from the PHR app, or a consent
notify). The
Authorizationheader is redacted inabdm_callbacks.headers, so capture it at the edge (load balancer or a temporary log line) and remove the capture afterwards. - Decode the token's payload (the middle segment, base64url). Do not paste the token anywhere public: it is a live gateway credential until it expires.
- Note
iss,aud(if any), and any claim whose value equals theX-HIP-IDorX-HIU-IDheader of that same request. - Repeat for an HIU callback (
X-HIU-ID) and confirm the same claim path holds the HIU id. - Set the values above on staging, run the manual test plan in #2416, then set them in production.
Body fields checked against the header
| Webhook controller | Action | Body field |
|---|---|---|
hip_link_webhooks | profile_share | metaData.hipId |
hip_link_webhooks | token, add-contexts, auth, context and SMS notify, running token status | none |
hip_consent_webhooks | consent_notify | notification.consentDetail.hip.id |
hip_data_webhooks | on_request | none (consent id only) |
hip_discovery_webhooks | discover | hip.id or hipId (older shapes; the v3 body has neither) |
hip_discovery_webhooks | link_init, link_confirm | none |
hiu_consent_webhooks | on_fetch | consent.consentDetail.hiu.id |
hiu_consent_webhooks | consent_notify, on_init, on_status | none |
hiu_data_webhooks | data_notification, on_request | none (statusNotification.hipId names the sending HIP, not us) |
A body without the field passes. A body whose field names a different service than the header gets
401. Fields come from the ABDM v3 specs in docs/abdm/specs/.