Age Verification

Video selfie + card verification for regulatory compliance

System Architecture

Two-step verification combining video selfie analysis with optional card verification.

flowchart TB subgraph Mobile["Mobile App"] UI["Age Verification UI"] Camera["Camera Capture"] CardForm["Card Entry Form"] end subgraph Backend["MiFamilias Backend"] API["API Gateway"] Service["Age Verification Service"] Sessions["Session Store"] DB["DynamoDB"] end subgraph AWS["AWS Services"] Rekognition["AWS Rekognition\n- Face Detection\n- Age Estimation\n- Liveness Check"] end subgraph Stripe["Stripe"] SetupIntent["SetupIntent API\n- Card Verification\n- No Charge"] end UI --> Camera Camera --> API API --> Service Service --> Sessions Service --> Rekognition Service --> SetupIntent Service --> DB UI --> CardForm CardForm -.->|Direct to Stripe| SetupIntent

Video Selfie Verification Flow

Primary verification method using AWS Rekognition for age estimation.

sequenceDiagram participant User participant App participant API participant Service as Age Verification Service participant Rekognition as AWS Rekognition participant DB as DynamoDB User->>App: Start verification App->>API: POST /verification/age/session API->>Service: createSession(userId) Service-->>API: sessionId, expiresAt API-->>App: Session created App->>App: Show camera with liveness prompts Note over App: "Look at camera" Note over App: "Blink your eyes" Note over App: "Turn head left/right" App->>App: Capture 12 frames over 4 seconds App->>API: POST /verification/age/video Note right of App: sessionId + base64 frames API->>Service: analyzeVideoFrames(sessionId, frames) loop Each Frame Service->>Rekognition: DetectFaces(frame) Rekognition-->>Service: FaceDetails, AgeRange end Service->>Service: Aggregate age estimation Service->>Service: Check liveness (blink, movement) alt Age clearly 18+ AND liveness passed Service-->>API: verified: true API-->>App: Verification successful App->>API: POST /verification/age/confirm API->>Service: completeVideoVerification Service->>DB: Update user profile DB-->>Service: ageVerified: true API-->>App: Confirmed App-->>User: Age Verified! else Age borderline (16-20) OR liveness uncertain Service-->>API: requiresCardVerification: true API-->>App: Card verification needed App-->>User: Additional verification required else Verification failed Service-->>API: verified: false API-->>App: Please try again end

Card Verification Flow

Backup verification using Stripe SetupIntent (no charge).

sequenceDiagram participant User participant App participant API participant Service as Age Verification Service participant Stripe participant DB as DynamoDB Note over User,App: After video analysis requires card verification User->>App: Proceed with card verification App->>API: POST /verification/age/card Note right of App: sessionId API->>Service: createCardVerification(sessionId) Service->>Stripe: Create SetupIntent Note right of Service: payment_method_types: ['card'] Note right of Service: usage: 'off_session' Stripe-->>Service: clientSecret, setupIntentId Service-->>API: clientSecret API-->>App: Show card form App->>App: Display Stripe Elements User->>App: Enter card details Note over User,App: Card data goes directly to Stripe App->>Stripe: Confirm SetupIntent Note right of App: $0 authorization only Stripe-->>App: SetupIntent succeeded App->>API: POST /verification/age/confirm Note right of App: sessionId, setupIntentId API->>Service: confirmCardVerification Service->>Stripe: Retrieve SetupIntent Stripe-->>Service: status: succeeded Service->>DB: Update user profile Note right of Service: ageVerified: true Note right of Service: method: video_and_card DB-->>Service: Updated Service-->>API: verified: true API-->>App: Verification complete App-->>User: Age Verified!

Liveness Detection

Multi-frame analysis to ensure real person, not a photo.

flowchart TB subgraph Input["Video Frames (12 frames)"] F1["Frame 1"] F2["Frame 2"] F3["..."] F12["Frame 12"] end subgraph Checks["Liveness Checks"] C1["Single Face Check\n- No multiple faces\n- Consistent face ID"] C2["Eye Blink Detection\n- EyesOpen varies\n- At least one blink"] C3["Mouth Movement\n- MouthOpen varies\n- Natural movement"] C4["Head Pose Variation\n- Yaw > 5 degrees\n- Pitch > 5 degrees"] end subgraph Result["Liveness Result"] Pass["PASS\n2+ checks passed\nNo multiple faces"] Fail["FAIL\nLess than 2 checks\nor multiple faces"] end F1 & F2 & F3 & F12 --> C1 & C2 & C3 & C4 C1 -->|Single face| Pass C1 -->|Multiple faces| Fail C2 & C3 & C4 -->|2+ passed| Pass C2 & C3 & C4 -->|Less than 2| Fail

Decision Flow

How verification outcome is determined.

flowchart TB Start([Video Submitted]) --> FaceDetect{Face Detected?} FaceDetect -->|No| Retry[Ask to retry] FaceDetect -->|Yes| LivenessCheck{Liveness Passed?} LivenessCheck -->|No| CardRequired[Require Card Verification] LivenessCheck -->|Yes| AgeCheck{Age Range?} AgeCheck -->|Low >= 18| Verified[Verified via Video Only] AgeCheck -->|Low 16-17| CardRequired AgeCheck -->|Low < 16| Blocked[Cannot Verify - Too Young] CardRequired --> CardVerify{Card Valid?} CardVerify -->|Yes| VerifiedCard[Verified via Video + Card] CardVerify -->|No| Retry Verified --> UpdateDB[(Update User Profile)] VerifiedCard --> UpdateDB style Verified fill:#2ecc71 style VerifiedCard fill:#2ecc71 style Blocked fill:#e74c3c style Retry fill:#f39c12

API Endpoints

Method Endpoint Description
GET /verification/age/status Get user's current verification status
POST /verification/age/session Create new verification session (30 min expiry)
POST /verification/age/video Submit video frames for analysis
POST /verification/age/card Create Stripe SetupIntent for card verification
POST /verification/age/confirm Confirm and persist verification

Data Retention

Data Type Stored? Duration Notes
Verification Status Yes Permanent ageVerified, method, verifiedAt on user profile
Video Frames No - Analyzed and discarded immediately
Biometric Data No - Rekognition processes but doesn't retain
Card Details No - Handled entirely by Stripe
Session Data Temporary 30 minutes In-memory, expires automatically

Debug Flags

Environment variables for development testing:

Variable Effect
AGE_VERIFY_BYPASS_REKOGNITION=true Skip AWS Rekognition, return mock age 25-35, liveness passes
AGE_VERIFY_BYPASS_STRIPE=true Skip Stripe verification, auto-succeed card check
AGE_VERIFY_FORCE_CARD=true Always require card verification step (for testing)

Third-Party Services

Service Purpose Data Handling
AWS Rekognition Face detection, age estimation, attribute analysis Processes frames, doesn't retain biometric data
Stripe Payment method verification ($0 authorization) PCI-DSS Level 1 certified, we never see card numbers

Regulatory Compliance

Region Requirement Our Implementation
Australia 16+ for social media (Social Media Ban Act) Video + card verification for borderline ages
UK Age-appropriate design (Online Safety Act) Age estimation with fallback verification
EU GDPR parental consent for under-16 Guardian verification for minors
Global COPPA compliance (under-13 protection) Block under-13 accounts