Skip to main content

Overview

The End-User Module provides citizens with secure self-service access to their digital identity and documents. Authentication leverages Hyperledger Indy verifiable credentials for privacy-preserving, cryptographically secure login without passwords.

Indy Authentication

Passwordless login using verifiable credential proofs

Self-Registration

Email-verified registration with optional TOTP MFA

Access Requests

Approve or reject issuer access requests

Document Access

View and download personal documents from Fabric ledger

Security & Authentication

Role Required: ROLE_USER Access Paths:
  • /user/** - User dashboard and features
  • /login/user - Login UI
  • /register/user - Registration flow
The end-user module implements the most sophisticated authentication system in CCDigital, combining:
  1. Hyperledger Indy Present-Proof 2.0 - Verifiable credential presentation
  2. Multi-Factor Authentication - Optional TOTP or email OTP
  3. Email Verification - OTP-based email confirmation during registration
  4. Password Recovery - Secure forgot-password workflow

Registration Flow

User Self-Registration

Citizens can register for platform access if they have an existing person record created by an administrator.
Important: Registration does NOT create a new person record. The person must already exist in the system (created via Admin module). Registration creates a user account (users table) linked to the existing person.
Endpoints:
  • Form: GET /register/user
  • Submit: POST /register/user
Controller: UserRegistrationController (src/main/java/co/edu/unbosque/ccdigital/controller/UserRegistrationController.java) Service: UserRegistrationFlowService
1

Access Registration Form

Navigate to /register/user to display the registration form.Controller: UserRegistrationController (UserRegistrationController.java:77)
2

Fill Registration Data

Provide required information:
  • ID type and number (must match existing person)
  • First and last name
  • Email address
  • Password (with strong validation)
  • Optional: Enable TOTP multi-factor authentication
Form DTO: UserRegisterForm
3

Email Verification

System sends OTP code to provided email address.Configuration:
  • Code length: APP_SECURITY_REGISTER_EMAIL_OTP_CODE_LENGTH
  • TTL: APP_SECURITY_REGISTER_EMAIL_OTP_CODE_TTL_MINUTES
  • Max attempts: APP_SECURITY_REGISTER_EMAIL_OTP_MAX_ATTEMPTS
  • Resend cooldown: APP_SECURITY_REGISTER_EMAIL_OTP_RESEND_COOLDOWN_SECONDS
4

Confirm Email OTP

Enter the OTP code received via email to verify email ownership.Endpoint: POST /register/user/email-otp/resendController: UserRegistrationController (UserRegistrationController.java:109)
5

Optional TOTP Setup

If TOTP was enabled during registration:
  • QR code is displayed for authenticator app
  • Enter TOTP code to confirm setup
Endpoint: POST /register/user/totp/confirmController: UserRegistrationController (UserRegistrationController.java:95)
6

Account Creation Complete

Upon successful verification:
  • User account is created in users table
  • Account is linked to existing person record
  • Access state is automatically synced to Indy (if enabled)
  • User can now authenticate via Indy proof

Registration Requirements

Person Linkage:
  • Person record must exist in persons table
  • ID type and number must match exactly
  • Person cannot already have a linked user account
Password Validation:
  • Strong password requirements enforced
  • Normalization and validation by UserRegistrationFlowService
Email Uniqueness:
  • Email must be unique across users
  • Verified via OTP before account creation
Automatic Sync:
  • After successful registration, active state synced to Indy
  • Enables immediate credential-based authentication

Authentication Flow

The end-user authentication system uses Hyperledger Indy Present-Proof 2.0 protocol instead of traditional passwords.

Indy-Based Login

Endpoints:
  • UI: GET /login/user
  • Start proof: POST /user/auth/start
  • Poll proof: GET /user/auth/poll
  • Verify OTP: POST /user/auth/otp/verify
  • Resend OTP: POST /user/auth/otp/resend
Services:
  • IndyProofLoginService - Proof request and verification
  • UserAuthFlowService - Complete authentication orchestration
1

Initiate Login

User accesses /login/user login page.System calls POST /user/auth/start to initiate proof request.
2

Present-Proof Request

Backend creates present-proof 2.0 request via ACA-Py:Required Attributes:
  • id_type - ID document type
  • id_number - ID document number
  • first_name - First name
  • last_name - Last name
  • email - Email address
Service: IndyProofLoginService using IndyAdminClient
3

User Presents Credential

User scans QR code or uses deep link to present credential from mobile wallet.Frontend polls GET /user/auth/poll to check proof status.Configuration:
  • Poll interval: ACAPY_PROOF_POLL_INTERVAL_MS
  • Poll timeout: ACAPY_PROOF_POLL_TIMEOUT_MS
4

Verify Proof

ACA-Py verifies the presented proof:
  • Validates credential signature
  • Checks credential hasn’t been revoked
  • Extracts verified attributes
Service: IndyProofLoginService#extractVerifiedAttributes
5

Second Factor (MFA)

If user has TOTP or email OTP enabled, second factor is required:TOTP Configuration:
  • Issuer: APP_SECURITY_TOTP_ISSUER
  • Digits: APP_SECURITY_TOTP_CODE_DIGITS
  • Period: APP_SECURITY_TOTP_PERIOD_SECONDS
  • Window: APP_SECURITY_TOTP_WINDOW_STEPS
Email OTP Configuration:
  • Code length: APP_SECURITY_LOGIN_OTP_CODE_LENGTH
  • TTL: APP_SECURITY_LOGIN_OTP_CODE_TTL_MINUTES
  • Max attempts: APP_SECURITY_LOGIN_OTP_MAX_ATTEMPTS
  • Resend cooldown: APP_SECURITY_LOGIN_OTP_RESEND_COOLDOWN_SECONDS
6

Create Session

Upon successful proof verification and MFA:
  • Spring Security session is created
  • User principal is set to IndyUserPrincipal
  • User is redirected to /user/dashboard
Access State Validation: Users with access state SUSPENDED or DISABLED cannot authenticate, even with valid credentials. Access state is checked after proof verification and synchronized from admin module.

Authentication Architecture

Key Components:
  1. IndyAdminClient - ACA-Py admin API integration
  2. IndyProofLoginService - Proof request lifecycle management
  3. UserAuthFlowService - Complete authentication orchestration
  4. UserAccessGovernanceService - Access state synchronization
  5. IndyUserPrincipal - Security principal with verified attributes
Security Features:
  • No Password Storage: System never stores or transmits passwords for Indy login
  • Cryptographic Verification: Proof verification via zero-knowledge proofs
  • Revocation Checking: Credentials can be revoked at issuer level
  • Rate Limiting: Sensitive endpoints protected by SensitiveEndpointRateLimitFilter
  • Session Management: Configurable timeout with keepalive/expire endpoints

User Dashboard

After successful authentication, users access their personal dashboard. Endpoint: GET /user/dashboard Controller: UserController (src/main/java/co/edu/unbosque/ccdigital/controller/UserController.java:56)

Dashboard Features

Dashboard shows verified attributes from Indy credential:
  • Display name (first + last name)
  • ID type and number
  • Email address
  • TOTP MFA status (enabled/disabled, confirmation timestamp)
Source: IndyUserPrincipal from authentication context
User’s documents are queried directly from Hyperledger Fabric ledger:Service: FabricLedgerCliService#listDocsViewScript: Calls Node.js script configured via FABRIC_LIST_DOCS_SCRIPTParameters: User’s ID type and ID numberDocuments displayed include:
  • Document ID and type
  • Issue date and expiry date
  • Current status
  • Blockchain reference for traceability
Controller: UserController (UserController.java:63)
Users can view and manage access requests from issuers:Endpoint: GET /user/requests (referenced in README.md:170)Actions:
  • View pending access requests
  • Approve requests to grant issuer access
  • Reject requests to deny access
  • View request history and status
Data Model:
  • access_requests table stores request metadata
  • access_request_items links specific documents
  • consents table tracks approval/rejection decisions
View and download approved documents:Endpoints:
  • View: GET /user/docs/view/{docId} (README.md:171)
  • Download: GET /user/docs/download/{docId} (README.md:171)
Security:
  • Signed URLs with TTL (via SignedUrlService)
  • Path validation against allowed directories
  • Access event logging to Fabric audit trail
Configuration:
  • Secret: APP_SECURITY_SIGNED_URLS_SECRET
  • TTL: APP_SECURITY_SIGNED_URLS_TTL_SECONDS

TOTP Management

Endpoint Base: /user/mfa/totp/* (README.md:172) Features:
  • Enable/disable TOTP authentication
  • Regenerate TOTP secret
  • View QR code for authenticator apps
  • Confirm TOTP setup with test code
TOTP Standards:
  • Compliant with RFC 6238 (TOTP)
  • Compatible with Google Authenticator, Authy, etc.
  • Configurable code length, period, and window

Password Recovery

Endpoint Base: /user/auth/forgot/* (README.md:173) Flow:
1

Request Reset

User provides email address or ID number.
2

Send Recovery Code

System sends OTP code via email.Configuration:
  • Code length: APP_SECURITY_FORGOT_PASSWORD_CODE_LENGTH
  • TTL: APP_SECURITY_FORGOT_PASSWORD_CODE_TTL_MINUTES
  • Max attempts: APP_SECURITY_FORGOT_PASSWORD_MAX_ATTEMPTS
  • Resend cooldown: APP_SECURITY_FORGOT_PASSWORD_RESEND_COOLDOWN_SECONDS
  • Mail from: FORGOT_PASSWORD_MAIL_FROM
3

Verify Code

User enters received code to verify identity.
4

Reset Password

User creates new password with strong validation.
Password recovery only applies to the initial registration password. Indy-based authentication does not use passwords after initial setup.

Access Request Workflow

End users control which issuers can access their documents through the access request approval system.

Approval Process

1

Issuer Creates Request

Issuer submits access request for specific approved documents.
2

User Notification

User sees pending request in dashboard (/user/requests).
3

Review Request Details

User reviews:
  • Requesting entity information
  • Which documents are requested
  • Purpose of access (if provided)
  • Validity period
4

Approve or Reject

User decides to grant or deny access.Approval:
  • Request status changes to APROBADA
  • Consent record created in consents table
  • Issuer can view/download approved documents
  • Access events logged to blockchain
Rejection:
  • Request status reflects rejection
  • Issuer cannot access documents
  • User can provide rejection reason
Users maintain full control over document access:
  • Granular Control: Approve/reject specific documents, not all-or-nothing
  • Time-Limited: Access requests can have validity periods
  • Revocable: Users can revoke previously granted access
  • Auditable: All access tracked in blockchain audit trail

Data Privacy & Security

Personal Data Protection

Minimal Disclosure

Indy proofs reveal only required attributes, nothing more

No Password Storage

Credential-based auth eliminates password breach risk

User Consent

Explicit approval required for all document access

Audit Trail

Complete access history in immutable blockchain ledger

Security Headers & Policies

Configured in: SecurityConfig
  • Content Security Policy (CSP): Prevents XSS attacks
  • HSTS: Enforces HTTPS connections
  • X-Frame-Options: Prevents clickjacking (same-origin only)
  • Referrer-Policy: Controls referrer information disclosure
  • Rate Limiting: Protects against brute force attacks

Session Security

  • Inactivity Timeout: Configurable via SERVER_SESSION_TIMEOUT
  • Server-Side Sessions: Session data stored securely on server
  • Keepalive Endpoint: Prevents timeout during active use
  • Explicit Expiration: Session expire endpoint for logout

Endpoint Reference

HTTP MethodEndpointDescriptionSource
GET/register/userRegistration formUserRegistrationController.java:77
POST/register/userSubmit registrationUserRegistrationController.java:86
POST/register/user/totp/confirmConfirm TOTP setupUserRegistrationController.java:95
POST/register/user/email-otp/resendResend email OTPUserRegistrationController.java:109
GET/login/userLogin UIREADME.md:167
POST/user/auth/startStart Indy proofREADME.md:168
GET/user/auth/pollPoll proof statusREADME.md:168
POST/user/auth/otp/verifyVerify MFA OTPREADME.md:168
POST/user/auth/otp/resendResend MFA OTPREADME.md:168
GET/user/dashboardUser dashboardUserController.java:56
GET/user/requestsAccess requestsREADME.md:170
GET/user/docs/view/{docId}View documentREADME.md:171
GET/user/docs/download/{docId}Download documentREADME.md:171
*/user/mfa/totp/*TOTP managementREADME.md:172
*/user/auth/forgot/*Password recoveryREADME.md:173

Configuration

Key environment variables for the end-user module:
# ACA-Py Integration
ACA PY_VERIFIER_ADMIN_URL=http://verifier:8021
ACAPY_HOLDER_ADMIN_URL=http://holder:8031
ACAPY_CRED_DEF_ID=Th7MpTaRZVRYnPiabds81Y:3:CL:...
ACAPY_PROOF_POLL_INTERVAL_MS=2000
ACAPY_PROOF_POLL_TIMEOUT_MS=300000

# Indy Sync
INDY_USER_ACCESS_SYNC_ENABLED=true
INDY_USER_ACCESS_SYNC_PATH=/metadata/access_state

# TOTP Settings
APP_SECURITY_TOTP_ISSUER=CCDigital
APP_SECURITY_TOTP_DIGITS=6
APP_SECURITY_TOTP_PERIOD_SECONDS=30

# Email OTP Settings
MAIL_HOST=smtp.gmail.com
MAIL_PORT=587
MAIL_USERNAME=[email protected]
MAIL_PASSWORD=your-app-password

# Session Configuration
SERVER_SESSION_TIMEOUT=30m
See README.md section 9 for complete configuration reference.

Best Practices

  1. Enable TOTP: Always enable TOTP for enhanced security
  2. Review Requests Carefully: Check issuer identity before approving access
  3. Monitor Access: Regularly review access request history
  4. Secure Email: Use a secure email account for OTP delivery
  5. Keep Credentials Safe: Protect mobile wallet and authenticator app
  6. Report Issues: Contact administrators for suspicious activity

Troubleshooting

Cannot Login with Credential

Possible Causes:
  • Access state is SUSPENDED or DISABLED
  • Credential has been revoked by issuer
  • ACA-Py agents are not connected
  • Credential definition mismatch
Resolution:
  • Contact administrator to check access state
  • Verify credential in mobile wallet is valid
  • Ensure network connectivity for proof exchange

Email OTP Not Received

Possible Causes:
  • Email in spam/junk folder
  • SMTP configuration issue
  • Email address typo during registration
Resolution:
  • Check spam folder
  • Use resend OTP functionality
  • Contact support if persistent

TOTP Code Rejected

Possible Causes:
  • Time synchronization issue
  • Wrong authenticator app entry
  • Code expired (30-second window)
Resolution:
  • Ensure device time is synchronized
  • Re-scan QR code to set up authenticator
  • Generate fresh code and try again

Build docs developers (and LLMs) love