For the complete documentation index, see llms.txt. This page is also available as Markdown.

Agent Portal

A separate portal for field agents, authenticated against its own Keycloak 'agent' realm. Its first capability is Verifiable Credential issuance; it is built to carry further agent tasks.

The Agent Portal is a second staffed surface on the Registry Platform, alongside the Staff Portal. It exists for a different audience doing a different job: a field agent standing at a counter with a citizen in front of them, rather than a back-office user working through a queue.

It ships with the platform, so every registry manifestation (Farmer Registry, National Social Registry, …) inherits it. It is off by defaultagentPortalApi.enabled and agentPortalUi.enabled — because a deployment that has no agents should expose no agent surface.

A generic portal, not a credential tool

Today the portal carries exactly one capability: issuing a Verifiable Credential to an authenticated beneficiary. That is the first task, not the definition of the product.

The landing page is therefore a list of tasks, one card per capability, rather than the issuance form itself. Adding a capability means adding a card; it does not mean restructuring the application. A task the signed-in agent is not permitted to perform is shown disabled with the reason, rather than hidden — an agent who was told they can do something needs to see why they cannot.

Why a separate agent realm

Agents authenticate against a Keycloak realm of their own — agent — not the staff realm.

They are a genuinely different population: typically far more numerous, often contracted rather than employed, and entitled to a much narrower set of actions. Keeping them in their own realm means a staff account grants nothing in the agent portal and an agent account grants nothing in the staff portal, without either side having to be careful about role naming.

Staff
Agent

Keycloak realm

staff

agent

Confidential client

staff-portal

agent-portal

Login theme

staff-portal

agent-portal

Permission

the full registry catalogue

register:issue_credential

The realm, its confidential client and the client's Secret are created by commons-services' keycloak-init, exactly as the staff realm is. The registry chart adds only the demo agent user. This split matters: the client is confidential and iam-agent-portal-api is its only consumer, so the client and its Secret have to come from the release that configures that API.

keycloak-init never resets an existing user's password, and the realm lives in the Keycloak database, which outlives a registry reinstall. Changing global.agentUserPassword therefore only takes effect on a realm that does not yet have that user.

How authentication works

The Agent Portal uses the same model as the Staff Portal — deliberately, so there is one authentication design in the platform rather than two.

The UI is a Next.js application whose /api/* routes are a Backend-For-Frontend (BFF). The browser never holds a token:

IAM is the OIDC confidential client. It holds the client secret, performs the code exchange, owns the session, and refreshes tokens silently. The browser sees only httpOnly cookies it cannot read.

The agent and staff portals share a parent cookie domain (.<namespace>.<domain>), and browsers match cookies by domain, not origin — so each portal receives the other's cookies. With identical names the second login silently overwrites the first, and each portal's API is then handed the other realm's tokens, which it cannot verify.

The agent portal's cookies are therefore prefixed (agent-X-Access-Token, agent-X-ID-Token, agent-X-Session-Id, agent-X-CSRF-Token) via IAM_AGENT_AUTH_COOKIE_PREFIX. Staff keeps the unprefixed names. Both the IAM service that sets the cookies and the Agent Portal API that reads them must be configured with the same prefix.

Signing out

Sign-out is RP-initiated OIDC logout: the portal calls /api/logout, IAM clears the session cookies and redirects to Keycloak's end_session_endpoint, and Keycloak returns the browser to post_logout_redirect_uri — which IAM takes from the login provider's default_redirect_uri.

Permissions

Roles live on the agent-portal Keycloak client and are resolved into permissions by the staff IAM API — /user-access/* exists only there. This requires two things to line up, and returns an empty list (with HTTP 200) if either is missing:

  1. the agent's roles under resource_access['agent-portal'] in the token, and

  2. an IAM application registered with mnemonic agent-portal.

The registry's iam-register Job registers that application, declaring the register:issue_credential and register:verify_credential permissions and the roles that grant them. The two are separate on purpose: checking a card someone presents and creating a new one are different acts with different risk, and a deployment may well want staff who can do the first and not the second.

Current functionality: Verifiable Credential issuance

The agent's job is to establish that the person in front of them is who they claim to be, and then hand them a credential. That is three steps, and the portal presents them on one screen because the citizen is waiting and the authentication expires in minutes.

Step 2 is the point of the whole flow. The agent is authenticated by their own token on every call; the beneficiary is authenticated by eSignet, and that authentication is re-checked at the moment of issue rather than trusted from an earlier screen — the window may well have elapsed while the agent was reading.

Issuing more than one credential type

A registry can define several credential types — vcDefinitions is a list, and each entry carries its own registry view, claim columns, Certify config, card SVG and qr_data_label. No code change is needed to add one.

The agent picks the credential in step 1, before the look-up, and the choice is locked once a record is found. That ordering is not cosmetic: the definition names the view the beneficiary is resolved through, so choosing later would mean looking someone up in one view and issuing them a credential built from another. lookup_beneficiary, start_authentication and issue therefore all carry vc_type.

The selector only appears when more than one type is configured; with a single definition the flow is unchanged.

Current functionality: Verifiable Credential verification

A second card, Verify VC, gated by register:verify_credential.

The agent uploads the printed credential (PDF) or a photo of it, and gets a verdict plus the credential's contents.

The image never leaves the agent's device. A photo of a credential is a photo of someone's identity document; uploading it would put that image in request logs, proxies and every intermediary in between. Only the signed payload the QR already encodes is transmitted.

Showing the contents is the point, not decoration. "Valid" alone says the card is genuine without saying whose it is, so it cannot catch a real credential presented by the wrong person. The fields shown are decoded from the signed payload — exactly what was signed — and only after a SUCCESS.

Every verification is audited with its verdict, so the trail distinguishes a genuine card from a forged one. See Verification.

For the full design — what each step guarantees, what the QR contains, which signature a verifier checks, and how a registry supplies its claims — see Verifiable Credential Issuance and in particular Phase 1 — Paper Credential.

Components

Component
Repo
Role

agent-portal-ui

Registry Platform

Next.js app; its /api/* routes are the BFF

agent-portal-api

Registry Platform

Resolves the record, drives the beneficiary's authentication, pushes claims to Certify, renders the PDF, logs the issuance

iam-agent-portal-api

IAM

OIDC confidential client for the agent realm

A variant registry rebuilds agent-portal-api from the platform image with its own extension installed, exactly as it does for staff-api — the service maps the registry's own register-domain model, and running the platform's default model against a variant's database is a schema mismatch.

Extending the portal

A new agent capability needs:

  1. a permission on the agent-portal client, declared in the registry's iam-register payload;

  2. routes on the Agent Portal API, guarded by that permission;

  3. BFF routes in the UI that proxy them, so the browser still holds no token;

  4. a card on the landing page, enabled by that permission.

Nothing about the authentication model changes.

Last updated

Was this helpful?