Released docs. You are viewing the documentation published with v0.34.0. Development docs are available at Latest.
Authenticate citizens with eSignet over Base Registry Engine (BReg)
You run a population or civil registry on Base Registry Engine (BReg) and you want citizens to sign in to public services with eSignet, the open-source identity and authentication service from the MOSIP project. The eSignet BReg provider is the piece that connects the two. It lets eSignet resolve a verified identifier against your registry through one governed lookup, and release only the demographic claims each citizen approves. This page explains what the provider does, what your registry must grant it, and what it deliberately leaves out. It is the adopter-facing account of registrystack/esignet-relay-authenticator v0.3.0.
Who owns what
Section titled “Who owns what”eSignet owns the login experience and the OpenID Connect (OIDC) surface: the relying-party registration, the authorization and token endpoints, the consent screen, and the signing and optional encryption of ID Tokens and UserInfo responses. Relying parties talk to eSignet only. They never see BReg.
BReg owns the registry: the population records, the access profiles that decide which client may read which fields, the revision history, and the audit journal. It knows nothing about eSignet as a product. From its side, the provider is one more client with one narrow grant.
The provider obtains a short-lived access token from the OAuth issuer your deployment configures.
It authenticates as a registered private_key_jwt client and fixes the BReg resource and scopes in
its own configuration, so a login request cannot widen them.
The provider itself is a Go module compiled into the eSignet host. eSignet selects it by setting the
authentication provider to breg and pointing it at one YAML configuration file. The provider
contributes three things: it verifies the citizen’s challenge, it performs the governed lookup, and it
derives the claims and the pairwise subject that eSignet then signs. Everything else stays with
eSignet.
The login flow
Section titled “The login flow”A login takes two round trips into your registry at most, and none until the citizen has proved possession of their identifier.
sequenceDiagram
participant C as Citizen
participant E as eSignet
participant P as BReg provider
participant I as OAuth issuer
participant B as BReg
C->>E: Enter identifier, receive challenge
C->>E: Submit challenge response
E->>P: Verify challenge
P->>I: private_key_jwt client_credentials
I-->>P: Short-lived access token
P->>B: Lookup account-check fields only
B-->>P: uin, status
E->>C: Consent screen (requested claims)
C->>E: Approve a subset
E->>P: Fetch approved claims
P->>B: Lookup approved fields only
B-->>P: Selected domain data
P-->>E: Claims plus pairwise subject
E-->>C: Signed ID Token and UserInfo
The order matters for data protection:
- Challenge first. The provider verifies the citizen’s one-time code or other challenge before any registry call. A wrong code never reaches BReg or the token issuer, so the registry is not an oracle for guessing identifiers.
- Account check with the smallest projection. After a correct challenge, the provider looks up
the identifier and asks BReg for only the fields named in
account_check_fields, typically the identifier and a status field. Which records resolve at all is the access profile’s decision: a profile scoped to active records makes inactive ones unresolvable, and the provider reports that as a denied subject. - Fresh consent every time. eSignet shows the consent screen on every login. The provider installs no remembered consent, so a citizen who approved their birthdate last month is asked again today.
- Second lookup limited to the approved intersection. The provider computes the intersection of
the claims the citizen approved, the claims named in
claim_map, and the fields the access profile provisions. It asks BReg for exactly that set. If the intersection is empty, it makes no call at all. - Only domain data becomes claims. The provider reads the record’s domain data and ignores envelope metadata such as revision numbers, timestamps, and links.
Authentication state between the challenge and consent steps is held server-side for five minutes and bound to the relying party, the client, and the transaction. A context that expires or is presented by a different party fails with a dedicated error code rather than a silent retry.
The governed lookup
Section titled “The governed lookup”The provider reaches your registry through a single fixed BReg operation: a lookup on the route you
name, with the selector you name, under the access profile you grant. On the wire that is a POST
to the route’s :lookup operation, with accessProfile and $select query parameters and a JSON
body carrying the selector and the verified identifier.
POST /v1/records/population:lookup?accessProfile=esignet-source&$select=uin,statusAuthorization: Bearer <access token>Content-Type: application/jsonAccept: application/json
{"selector":"by-uin","values":{"uin":"<verified identifier>"}}The $select list is never wider than the configured provisioned fields, and BReg enforces the
same ceiling on its side through the access profile. A misconfigured provider that asked for more
than the profile grants receives a denial, not extra data.
What your registry must grant
Section titled “What your registry must grant”On the BReg side, integration is an access-control decision expressed in the registry project, not a code change. The provider needs:
- One selector profile on the population route, for example
by-uin, that resolves exactly one record from one identifier field. The provider fails rather than picking among several matches. - One access profile dedicated to eSignet, for example
esignet-source, granting only thelookupoperation and only the fields the provider may ever project. Leaveget,list, and all write operations out of it. The provider never lists or writes, so a profile that permits either is wider than the integration needs. - A required scope only the provider holds.
accessProfileis a request parameter, so the profile’s name isolates nothing on its own: any authenticated caller whose token satisfies the profile’s principal claim, purposes, and row boundaries can select it. Give the profile arequiredScopesentry, for exampleregistry:esignet:lookup. A profile with no required scope raisesaccess.profile.no_required_scopewhen you check the project. - A field list that mirrors the profile. The provider’s
provisioned_fieldsmust match what the profile grants, andaccount_check_fieldsmust be a non-empty subset of it that contains the selector field. Keep the account check to the identifier and a status field. - A separate administrative client for anyone who edits records. The eSignet client’s key is lookup-only by construction, and it stays that way only if nobody reuses it for change requests.
Register the provider with your OAuth issuer as a private_key_jwt client using an ES256 or RS256
key. Configure the provider with the exact token endpoint, assertion audience, BReg resource, and
required scopes. The provider sends that immutable resource and scope set on every token request and
caches each returned token for at most five minutes.
The pages on BReg access profiles and governed registry actions explain the surrounding model.
Claims and the pairwise subject
Section titled “Claims and the pairwise subject”claim_map in the provider configuration ties each OIDC claim name to a registry field. Three
rules shape it:
- The subject is never a registry identifier.
claim_map.submust be the literal$psut. The provider derives a pairwise pseudonymous subject per relying party and client, as a base64url HMAC-SHA256 over the tuple of a fixed version string, the relying party, the client, the identifier type, and the identifier, keyed by a secret of at least 32 bytes read from a mounted file. Two relying parties therefore receive different subjects for the same citizen, and neither can recover the registry identifier from its value. - Only mapped fields can ever leave. A field that the access profile grants but
claim_mapomits is never requested. A claim the citizen approves that is not mapped is silently absent. - eSignet signs, the provider does not. The provider returns plain claims and the subject. Signing, JWE encryption for relying parties that request it, and key rotation are eSignet’s.
An eSignet ID Token or UserInfo JWT authenticates a citizen to a relying party. It is not a registry credential. BReg and Evidence Gateway verify access tokens against their configured issuer and required token type, so forwarding a citizen’s ID Token to either one is rejected. The only credential that reaches the registry is the provider’s own access token.
Failure behaviour
Section titled “Failure behaviour”The provider collapses registry-side failures into a fixed set of error codes so that a relying party or a citizen never learns whether an identifier exists, is inactive, or is concealed:
| Code | When |
|---|---|
registry_auth_invalid_request | The request to the provider is malformed or the configuration rejects it. |
registry_auth_challenge_failed | The one-time code or other challenge did not verify. No registry call was made. |
registry_auth_subject_denied | The lookup resolved no record the profile may disclose. |
registry_auth_unavailable | The token issuer or BReg did not answer within the configured timeout, returned an oversized body, redirected, or omitted a configured account-check field. |
registry_auth_context_invalid | The authentication context does not match the relying party, client, or transaction. |
registry_auth_context_expired | The five-minute authentication context has lapsed. |
Outbound calls require HTTPS unless you explicitly allow insecure HTTP for a local fixture. They carry a bounded timeout (10 seconds by default), a bounded response size (1 MiB by default), and follow no redirects. The provider never logs selectors, tokens, or source values.
A configuration example
Section titled “A configuration example”The full grammar lives with the provider. This example shows the shape and the relationships the earlier sections described.
subject_id_type: uinpsut_secret_file: /run/secrets/registry-psutbreg: base_url: https://population.example.org route: population selector: by-uin selector_field: uin access_profile: esignet-source provisioned_fields: [uin, status, givenName, familyName, birthdate, gender] account_check_fields: [uin, status]token_client: token_endpoint: https://issuer.example.org/oauth2/token assertion_audience: https://issuer.example.org/oauth2/token client_id: esignet-source private_key_file: /run/secrets/registry-client.jwk key_id: esignet-source-2026-09 resource: urn:breg:population scopes: [registry.read, records.lookup]claim_map: sub: $psut given_name: givenName family_name: familyName birthdate: birthdate gender: genderhttp: timeout_seconds: 10 max_response_bytes: 1048576demo: static_otp_enabled: falseSecrets arrive as mounted files, never as inline values or environment variables. The pairwise subject secret and the token client key are the two the deployment must protect and rotate on its own schedule.
The retired top-level mint key is rejected. Rename it to token_client and add the explicit
assertion_audience, key_id, resource, and nonempty scopes fields; there is no compatibility
alias and no client-secret mode.
The demo block enables a synthetic one-time code verifier that reads codes from a static file.
It exists for fixtures such as Solmara Lab. Production deployments supply a real challenge verifier
through the provider’s Go interface, and the host refuses to start with no verifier configured.
Boundaries
Section titled “Boundaries”Version 0.3.0 draws its lines deliberately:
- BReg only. The provider reads from a Base Registry Engine registry. Reading through Registry Relay is deferred, and Evidence Gateway is not involved: an eSignet login is an authentication, not a signed minimum-disclosure assertion.
- No production challenge delivery. Sending one-time codes over SMS or email, enrollment, wallet-based login, and identity assurance levels are out of scope. Deployments bring their own verifier.
- No Java path. Earlier releases shipped a Java authenticator and JAR injection. Version 0.3.0 replaces them with the Go provider, strict YAML, and mounted secret files, with no compatibility layer.
- Source-integrated build. The provider is compiled into a pinned eSignet release rather than loaded as a plugin, and ships as an OCI image with checksums, an SBOM, and provenance. Publishing that image and running it as a hosted service are separate operator actions.
- Configure BReg access profiles to define the lookup-only grant.
- BReg HTTP API reference for the lookup route and problem documents.
- Data minimization and purpose limitation for the principles this integration applies.