Detailed design

Registrant Authentication — Feature Design Document

Project: OpenG2P Registry Gen2 Feature: Registrant Authentication via OIDC/OAuth2.0 Status: Design / Pre-implementation Date: 2026-04-10


1. Overview

Registrant Authentication is a feature that allows staff operating the registry to authenticate a registrant (farmer, disabled person, vehicle owner, etc.) using the same OIDC/OAuth2.0 infrastructure as user login, but designed for in-portal authentication facilitation rather than self-service login.

Key Use Case:

  • Staff opens a registrant's record in the registry

  • Staff clicks "Authenticate Registrant" widget

  • Widget initiates authentication with the configured auth provider (Keycloak, eSignet, etc.)

  • Auth provider performs authentication (password, OTP, biometric, face, voice, etc.)

  • Registry verifies the returned token and stores authentication proof

  • Audit trail is maintained; registrant is notified

  • Authentication is valid for a configured period (e.g., 2 years)

  • When approaching expiry, system notifies registrant to re-authenticate

Architectural Key Insight: This feature reuses the IAM service's OIDC/OAuth2.0 core libraries (imported as a dependency) but operates completely independently from user login. Registrant authentication is:

  • Tied to a registrant record (internal_record_id), not a user session

  • Initiated by staff, not self-service

  • Stored in the registry database with full audit trail

  • Managed with explicit expiry and re-authentication workflows


2. Design Principles

  • Reuse, Don't Duplicate: Import IAM core libraries (OidcClient, token validation, adapters) as a dependency

  • Separation of Concerns: Registrant auth is entirely separate from user authentication and authorization

  • Audit First: Every authentication attempt (success/failure) is recorded with full context

  • Compliance Ready: Stores proof (token hash, claims, method) for verification and audits

  • Extensible Providers: Pluggable adapter pattern supports any OIDC/OAuth2.0 provider (Keycloak, eSignet, custom)

  • Graceful Expiry: Clear workflow for re-authentication with user notifications


3. Data Model

3.1 G2PRegistrantAuthenticationProvider

Configuration table for registrant authentication providers. Multiple providers can be active simultaneously, allowing registrants to choose their preferred authentication method.

Location: openg2p-registry-gen2-core/.../models/g2p_registrant_authentication_provider.py

Multi-Provider Support:

  • Multiple providers can be active for the same register (e.g., Keycloak + eSignet)

  • Registrant chooses which provider to use via widget dropdown

  • display_order controls the order in the UI dropdown

  • register_id allows different registers to have different available providers (or same providers)


3.2 G2PRegistrantAuthentication

Audit trail table. Records every authentication attempt (success or failure) for every registrant.

Location: openg2p-registry-gen2-core/.../models/g2p_registrant_authentication.py

Indexes: (register_id, internal_record_id), (internal_record_id, status), (expiry_at) (for finding expired authentications).


3.3 G2PRegisterDefinition Extension

Add authentication requirements to register metadata:


3.4 G2PRegisterAuthentication Mixin Base Class

Abstract base class for register types that support authentication. Domain registers opt-in to authentication by extending this mixin.

Location: openg2p-registry-gen2-core/.../models/g2p_register_authentication.py

3.5 Domain Register Extension (Example: Farmer)

Domain registers that require authentication extend both base classes:

Optional Feature:

  • If a domain register does not need authentication, it only extends G2PRegister

  • If it needs authentication, it extends both G2PRegister and G2PRegisterAuthentication

  • This separation keeps authentication optional and allows flexibility per domain

Denormalized Fields: These fields in the mixin enable fast queries like "which registrants need re-authentication notifications?" without joining to the G2PRegistrantAuthentication audit table.


4. Service Layer

4.1 G2PRegistrantAuthenticationService

Orchestrates the registrant authentication flow with support for multiple providers.

Location: openg2p-registry-gen2-core/.../services/g2p_registrant_authentication_service.py

4.2 Reuse from IAM

Import and wrap IAM core services:


5. Authentication Flow

5.1 Staff Selects Provider and Initiates Authentication

Step 1: Fetch Available Providers

Endpoint: GET /register-data/get-available-authentication-providers

Step 2: Initiate Authentication with Selected Provider

Endpoint: POST /register-data/authenticate-registrant

Flow:

5.2 Auth Provider Callback

Endpoint: GET /registrant-auth/callback?code=...&state=...


6. Adapter Pattern

Registrant authentication adapters are identical to user login adapters in IAM, reused as-is.

6.1 Keycloak Adapter

6.2 e-Signet Adapter

Key Point: These adapters extract authentication_method and claim_verifications — data specific to registrant authentication that user login doesn't need.


7. Controller Service and API Endpoints

7.1 Controller Service

Location: openg2p-registry-gen2-core/.../controller_services/g2p_registrant_authentication_controller_service.py

7.2 Staff Portal API Endpoints

Location: openg2p-registry-gen2-apis/openg2p-registry-staff-portal-api/.../g2p_registrant_authentication_controller.py


8. UI Widget

8.1 Registrant Authentication Widget

Location: openg2p-registry-gen2-ui-widgets/src/widgets/RegistrantAuthenticationWidget.tsx

A new React widget for the staff portal with provider selection:

UI Example:

8.2 Registrant Authentication Status Widget

Display current auth status:


9. Notification and Re-authentication Workflow

9.1 Periodic Re-authentication Check

Celery Beat Producer: registrant_authentication_expiry_beat_producer

Every hour, query for registrants approaching expiry:

9.2 Notification to Registrant

Celery Worker: registrant_authentication_expiry_notifier

Sends notification (SMS, email, push) to registrant:


10. Configuration

10.1 Registry Configuration

Add to G2PRegisterDefinition:

10.2 Provider Configuration

Set up multiple G2PRegistrantAuthenticationProvider records (one per authentication option):

Example 1: Keycloak (Password + OTP)

Example 2: eSignet (Biometric)

Multi-Provider Flexibility:

  • Different registers can have different available providers

  • Registrants choose their preferred authentication method

  • Providers are sorted by display_order in the UI

  • Can enable/disable providers independently via is_active flag

10.3 Environment Variables


11. Encryption and Security

11.1 User Claims Storage

User claims are encrypted at rest:

Uses a master encryption key managed via environment or KeyVault.

11.2 Token Hash (Proof, Not Storage)

Instead of storing the actual token, store a SHA-256 hash:

Purpose: Proof that authentication was performed, not for validation. Allows auditing without exposing the original token.

11.3 Security Features Reused from IAM

  • PKCE: Protects authorization code interception

  • Nonce: Prevents ID token replay attacks

  • State Parameter: Prevents CSRF

  • at_hash: Verifies access token integrity

  • HTTPS only: Secure cookie transmission

  • Token validation: Signature verification via JWKS


12. Audit Trail Features

The G2PRegistrantAuthentication table provides:

  1. Who: initiated_by_staff_id (which staff initiated)

  2. When: initiated_at, completed_at, created_at

  3. What: user_claims (who was authenticated), authentication_method (how)

  4. Proof: token_hash (hash of original token)

  5. Verification: claim_verifications (what was verified)

  6. Status: status (success/failure), failure_reason

  7. Validity: token_expires_at, expiry_at (when auth is no longer valid)

Compliance Benefits:

  • Non-repudiation: Staff can't deny initiating authentication

  • Evidence: Token hash proves token was received and validated

  • Auditability: Full timeline of every authentication attempt

  • Traceability: Links staff, registrant, provider, method


13. Comparison: User Login vs. Registrant Authentication

Aspect
User Login (IAM)
Registrant Auth (Registry)

Who

System user (staff, agent)

Beneficiary/registrant

Where

Login portal, self-service

Initiated by staff in-app

Why

Access the system

Prove identity for record

Storage

Secure HTTP-only cookie

Database record + encrypted

Lifespan

Session (hours)

Long-term (2 years)

Expiry

Implicit (session timeout)

Explicit, with warnings

Audit

User access logs

Authentication audit trail

Adapter

LoginProvider

RegistrantAuthenticationProvider

Reuse

OidcClient, token validation

OidcClient, token validation, adapters


13. Database Migrations

New Tables (Alembic migrations):

  • [ ] g2p_registrant_authentication_providers (provider configuration)

  • [ ] g2p_registrant_authentications (audit trail)

Updated Tables: For each domain register that supports authentication, add columns via migration:

  • last_authentication_id (FK)

  • last_authenticated_at (DateTime)

  • last_authentication_status (String enum)

  • authentication_expiry_at (DateTime, indexed)

  • authentication_expiry_notified (Boolean)

Example: When G2PRegisterFarmer extends G2PRegisterAuthentication, the g2p_register_farmers table automatically includes these columns.


14. Implementation Checklist

Core Service Layer:

  • [ ] Create models:

    • [ ] G2PRegistrantAuthenticationProvider (provider config)

    • [ ] G2PRegistrantAuthentication (audit trail table)

    • [ ] G2PRegisterAuthentication (mixin base class)

  • [ ] Create service: G2PRegistrantAuthenticationService

  • [ ] Create controller service: G2PRegistrantAuthenticationControllerService

  • [ ] Wrap IAM libraries: OidcClient, token validation, adapters

  • [ ] Implement AuthenticationSession (state/nonce/code_verifier, TTL)

  • [ ] Encryption/decryption for user_claims

API Endpoints (Staff Portal):

  • [ ] POST /register-data/authenticate-registrant/initiate

  • [ ] GET /registrant-auth/callback

  • [ ] POST /register-data/get-registrant-authentication-status

  • [ ] POST /register-data/get-registrant-authentication-history

UI Widgets:

  • [ ] RegistrantAuthenticationWidget (initiate button + popup)

  • [ ] RegistrantAuthenticationStatusWidget (display status)

Celery Tasks:

  • [ ] registrant_authentication_expiry_beat_producer

  • [ ] registrant_authentication_expiry_notifier

Migrations:

  • [ ] Alembic migrations for new tables (providers, authentications)

  • [ ] Add fields to G2PRegisterDefinition (authentication requirements)

  • [ ] Add columns to domain registers that extend G2PRegisterAuthentication mixin

    • Via inheritance, columns are auto-generated in extending tables

Configuration:

  • [ ] Setup G2PRegistrantAuthenticationProvider table

  • [ ] Create provider record (Keycloak/eSignet)

  • [ ] Configure register metadata (requires_registrant_authentication)

  • [ ] Environment variables for encryption key, callback URL

Testing:

  • [ ] Unit tests for authentication flows

  • [ ] Integration tests with mock auth provider

  • [ ] Encryption/decryption tests

  • [ ] Expiry notification logic


15. Architectural Highlights

Mixin-Based Design

The G2PRegisterAuthentication mixin base class embodies clean architecture principles:

Benefits:

  • Single Responsibility: G2PRegister stays focused on core functionality

  • Optional Feature: Only registers that need authentication extend the mixin

  • Consistency: Follows existing mixin pattern (G2PPerson, G2PGeo)

  • Flexibility: Different domains can choose to support authentication or not

  • Clean Tables: Database tables only include columns they actually use


Last updated

Was this helpful?