Implementation Guide

Technical documentation for the Lumin Identity verification flow and integration architecture

Current System Architecture

The current implementation is built as a standalone Next.js application deployed on Vercel. Here's how the system works today and its limitations.

How It Works Today
1

User Authentication

Users authenticate via Lumin OAuth (auth-dev.luminpdf.com) using the Authorization Code flow. Session data is stored in encrypted cookies using iron-session.

2

Identity Verification

For Passport credentials, users are redirected to Sedicii's verification flow. Sedicii uses zero-knowledge proofs to verify passport data without storing images. Verification status is tracked via polling and webhooks.

3

Credential Issuance

Credentials are issued via walt.id or MATTR using OpenID4VCI. The system generates a credential offer URL with a one-time PIN/transaction code.

4

Credential Delivery

Users receive a QR code (desktop) or deep link (mobile) to claim their credential in a compatible wallet app (MATTR GO Hold, walt.id wallet, etc.).

Key Technologies

Next.js 15OAuth 2.0OpenID4VCISedicii APIMATTR VIIwalt.idiron-session
Current Limitations

No Persistent Storage

All state is stored in encrypted session cookies. There's no database to track verification history, issued credentials, or user profiles over time.

Limited Scalability

As a standalone application, it's difficult to share identity verification data with other Lumin services or build a unified user profile.

Single Provider Lock-in

Users must choose a specific identity verification provider (Sedicii) at runtime. There's no abstraction layer to support multiple providers seamlessly.

No Verification History

Once a user closes their browser, there's no way to retrieve their verification status or re-issue credentials without going through the entire flow again.

Manual Provider Integration

Each new identity provider requires manual code integration. There's no plugin architecture or standardized provider interface.

Implementing as a Lumin Core Service

By implementing identity verification as a core Lumin platform service, we can create a reusable, scalable system that serves all Lumin products and provides a consistent user experience across the entire platform.

Provider Selection Flow
A unified verification flow that supports multiple identity providers
1

Entry Point: /verify?success_url=

Users start the verification process by navigating to /verify with a success_url parameter that specifies where to redirect after successful verification.

https://luminidentity.vercel.app/verify?success_url=https://app.luminpdf.com/dashboard
2

Provider Selection

The page displays available identity verification providers with their characteristics:

Stripe Identity
• Fast verification (2-5 minutes)
• Government ID + selfie
• Best for: Banking, KYC compliance
• Cost: $1.50 per verification
Sedicii
• Privacy-first verification
• Zero-knowledge proofs
• Best for: Privacy-conscious users
• Cost: Enterprise pricing
CloudCheck
• Background checks
• Criminal record searches
• Best for: Employment verification
• Cost: Variable by check type
3

Provider Flow: /verify/[provider]?success_url=

After selecting a provider, users are redirected to the provider-specific flow. Each provider has its own implementation but follows a common pattern:

Stripe: /verify/stripe?success_url=...
Sedicii: /verify/sedicii?success_url=...
CloudCheck: /verify/cloudcheck?success_url=...
4

Provider Verification

The provider conducts their verification process (ID scan, selfie, background check, etc.). During this phase, the system may:

  • Redirect to the provider's verification interface
  • Embed an iframe for seamless experience
  • Poll for verification status
  • Listen for webhook callbacks
MongoDB User Schema
Persistent storage for user verification data

When verification is complete, the MongoDB user document is updated with detailed identity information:

{
  "_id": "user_abc123",
  "email": "max@example.com",
  "identity": {
    "verified": true,
    "idp": "sedicii",
    "provider_metadata": {
      "session_id": "abc123xyz",
      "verification_date": "2025-01-15T14:30:00Z"
    },
    "name": "Max Kristian Ferguson",
    "date_of_birth": "1990-05-15",
    "document_type": "passport",
    "document_number": "X12345678",
    "document_country": "IE",
    "verified_at": "2025-01-15T14:30:00Z",
    "verification_level": "high_assurance"
  },
  "credentials": [
    {
      "credential_id": "cred_xyz789",
      "type": "LuminPassport",
      "issuer": "did:web:luminidentity.vercel.app",
      "issued_at": "2025-01-15T14:31:00Z",
      "status": "active"
    }
  ],
  "created_at": "2025-01-10T10:00:00Z",
  "updated_at": "2025-01-15T14:31:00Z"
}

Key Fields

identity.verifiedBoolean flag indicating verification status
identity.idpIdentity provider used (stripe, sedicii, cloudcheck)
identity.nameVerified full name from identity document
identity.verification_levelAssurance level (basic, substantial, high_assurance)
MATTR Credential Issuance
Issue verifiable credentials after successful verification

After updating the user database, the system automatically issues a verifiable credential via MATTR:

1. Create Credential Offer

The system calls the MATTR API to create a credential offer with the verified user data:

POST https://development-sign.vii.au01.mattr.global/v2/credentials/web-semantic/offers
{
  "credentialConfigurationId": "lumin_passport_config",
  "claims": {
    "name": "Max Kristian Ferguson",
    "date_of_birth": "1990-05-15",
    "verified_by": "sedicii",
    "verification_level": "high_assurance"
  }
}

2. Generate Credential URI

MATTR returns a credential offer URI that can be displayed as a QR code or deep link:

openid-credential-offer://?credential_offer_uri=https://...
&transaction_code=123456

3. Display to User

The user scans the QR code with their MATTR GO Hold wallet (or any compatible wallet) to claim the credential. The credential is cryptographically signed and can be verified by any party that trusts the Lumin Identity issuer.

Supported Credential Formats

mDoc (ISO 18013-5)W3C Verifiable CredentialsOpenID4VCISD-JWT VC
Core Service Architecture
Lumin Products/verify?success_url=...
↓ User selects provider
/verify/stripe/verify/sedicii/verify/cloudcheck
↓ Provider verification
Update MongoDB user.identity
↓ Issue credential
MATTR credential issuance
↓ Redirect with status
success_url?verified=true

Key Benefits

Reusable Across Lumin: Any Lumin product can trigger verification with a simple redirect
Provider Agnostic: Easy to add new providers without changing client applications
Persistent Data: Verification history and credentials stored in MongoDB
Standards-Based: Uses W3C VCs, OpenID4VCI, and ISO 18013-5 standards
Implementation Checklist
Create provider abstraction layer - Define common interface for all identity providers
Implement provider selection page - Build UI at /verify
Add Stripe Identity integration - Implement /verify/stripe flow
Add CloudCheck integration - Implement /verify/cloudcheck flow
Set up MongoDB connection - Configure connection to Lumin platform database
Create database update logic - Update user.identity after successful verification
Integrate MATTR issuance - Automatically issue credentials after verification
Add success_url handling - Properly redirect users back to calling application
Write integration tests - Test full flow for each provider
Deploy as core service - Move from standalone app to Lumin platform infrastructure