Technical documentation for the Lumin Identity verification flow and integration 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.
Users authenticate via Lumin OAuth (auth-dev.luminpdf.com) using the Authorization Code flow. Session data is stored in encrypted cookies using iron-session.
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.
Credentials are issued via walt.id or MATTR using OpenID4VCI. The system generates a credential offer URL with a one-time PIN/transaction code.
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.).
All state is stored in encrypted session cookies. There's no database to track verification history, issued credentials, or user profiles over time.
As a standalone application, it's difficult to share identity verification data with other Lumin services or build a unified user profile.
Users must choose a specific identity verification provider (Sedicii) at runtime. There's no abstraction layer to support multiple providers seamlessly.
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.
Each new identity provider requires manual code integration. There's no plugin architecture or standardized provider interface.
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.
/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/dashboardThe page displays available identity verification providers with their characteristics:
/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:
/verify/stripe?success_url=.../verify/sedicii?success_url=.../verify/cloudcheck?success_url=...The provider conducts their verification process (ID scan, selfie, background check, etc.). During this phase, the system may:
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"
}identity.verifiedBoolean flag indicating verification statusidentity.idpIdentity provider used (stripe, sedicii, cloudcheck)identity.nameVerified full name from identity documentidentity.verification_levelAssurance level (basic, substantial, high_assurance)After updating the user database, the system automatically issues a verifiable credential via MATTR:
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"
}
}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=123456The 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.
/verify?success_url=.../verify/stripe/verify/sedicii/verify/cloudchecksuccess_url?verified=true/verify/verify/stripe flow/verify/cloudcheck flow