Skip to content

Integrate Didit identity verification (KYC) into the application #439

Description

@0xdevcollins

Implement end-to-end Didit identity verification so users can verify their identity from the app. The flow uses SDK sessions: the backend creates a verification session, the frontend opens the Didit SDK (modal), and the backend receives results via webhook and persists status on the user.

ETA: 12hrs

Scope: Frontend (Next.js) only. Backend already expose POST /api/didit/create-session and POST /api/didit/webhook, and to persist identityVerificationStatus and identityVerificationAt on the User model.


Implementation Summary

Layer Responsibility
Frontend API lib/api/didit.tscreateDiditSession() calls backend with shared api client (cookies). No Next.js API routes.
UI Settings → Identity tab: card showing status (Verified / Declined / In review) and a Verify identity button that opens the Didit SDK modal.
Types User (and API response types) include optional identityVerificationStatus and identityVerificationAt.

Backend Contract (Expectations)

Create session

  • Endpoint: POST /api/didit/create-session

  • Auth: Required (e.g. session cookie or Bearer). Backend uses authenticated user id as vendor_data unless user_id is sent in body.

  • Request body (optional):

    { "user_id": "<uuid>" }
  • Response: Standard API wrapper; session payload in data:

    {
      "success": true,
      "message": "Success",
      "data": {
        "session_id": "uuid",
        "session_number": 1,
        "session_token": "string",
        "url": "https://verify.didit.me/session/<token>",
        "vendor_data": "string",
        "metadata": null,
        "status": "Not Started",
        "callback": "https://your-frontend.com/api/didit/callback",
        "workflow_id": "uuid"
      },
      "meta": { "timestamp": "...", "requestId": "..." }
    }
  • Note: The verification URL is in data.url, not data.verification_url. Frontend must map urlverification_url when normalizing the response.

User / GetMe response

  • Endpoint: GET /users/me (or equivalent) must return a user object that includes:
    • identityVerificationStatus: "Approved" | "Declined" | "In Review" | null
    • identityVerificationAt: ISO date string or null (set when status becomes Approved)

Types and References

Frontend types

lib/api/types.ts and types/user.ts — User

// Add to User interface
/** Didit identity verification: Approved | Declined | In Review | null */
identityVerificationStatus?: 'Approved' | 'Declined' | 'In Review' | null;
identityVerificationAt?: string | null;

lib/api/didit.ts

  • Backend payload (internal): DiditSessionDatasession_id, session_token, url, status, optional session_number, vendor_data, metadata, callback, workflow_id.
  • Public response: DiditCreateSessionResponsesession_id, session_token, verification_url (mapped from url), status.
  • Params: CreateDiditSessionParams — optional user_id?: string.

SDK usage

  • Package: @didit-protocol/sdk-web
  • API: Use DiditSdk.shared, set onComplete, then sdk.startVerification({ url: verification_url }).
  • onComplete result: { type: 'completed' | 'cancelled' | 'failed', session?: { sessionId, status }, error?: { message } }.

Files to Create

File Purpose
lib/api/didit.ts API client: createDiditSession(params?). Calls POST /api/didit/create-session, unwraps data, maps urlverification_url, throws if session_token or url missing.
components/didit/DiditVerifyButton.tsx Client component: button that calls createDiditSession(), initializes Didit SDK with DiditSdk.shared and onComplete, runs startVerification({ url }). Handles loading, error state, and callbacks onSuccess, onError, onCancel.
components/didit/IdentityVerificationSection.tsx Client component: card showing title/description, status badge (Verified / Declined / In review) and optional verified date, <DiditVerifyButton> when not Approved, and optional localhost hint (tunnel + FRONTEND_URL / DIDIT_CALLBACK_URL). Props: `user: GetMeResponse

Files to Modify

File Changes
app/me/settings/SettingsContent.tsx Add Identity tab to settings tabs. Add <TabsContent value="identity"> that renders <IdentityVerificationSection user={userData} onVerificationComplete={fetchUserData} />. Ensure fetchUserData is stable (e.g. useCallback) and passed so that after verification the user data (including identityVerificationStatus) is refetched.
lib/api/types.ts On User interface, add optional identityVerificationStatus?: 'Approved' | 'Declined' | 'In Review' | null and identityVerificationAt?: string | null.
types/user.ts Same as above: add identityVerificationStatus and identityVerificationAt to the User type.

Acceptance Criteria

  • User can open Profile SettingsIdentity and see current verification status (or “You have not completed identity verification yet”).
  • Verify identity button calls the backend to create a session (via createDiditSession()), then opens the Didit SDK modal with the returned verification_url.
  • On success, cancel, or error, the button leaves loading state and (on success) the parent can refetch user so the UI shows updated status.
  • Status is displayed as a badge: Verified (green), Declined (red), In review (amber). When Approved, show “Verified on <date>” if identityVerificationAt is set.
  • No Next.js API routes for Didit; all session creation goes through the existing backend via lib/api (shared api client with cookies).
  • When running on localhost, a short hint is shown: use a tunnel and set backend FRONTEND_URL or DIDIT_CALLBACK_URL to avoid browser blocking the callback (Private Network Access).

References

  • Didit: Quick Start, Web SDK, Webhooks
  • Backend (NestJS): src/modules/didit, src/config/didit.config.ts
  • Integration doc (this repo): DIDIT_INTEGRATION.md (flow, env vars, troubleshooting)

Dependencies

  • npm: @didit-protocol/sdk-web (already used by the implementation above).

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions