feat(verification): Verification Status & Compliance API (#74) - #85
Conversation
|
@josueazc is attempting to deploy a commit to the ManuelJG's projects Team on Vercel. A member of the Team first needs to authorize it. |
Adds a centralized, read-only Verification API that exposes KYC (user) and KYB (business) compliance status as the single source of truth, aggregating records across identity providers into one standardized response. - GET /v1/verification/user/:id — KYC status for an individual - GET /v1/verification/business/:id — KYB status for a business - Standardized response: isVerified, status, level, provider, expiresAt, lastUpdated - Provider-agnostic aggregation (highest valid level wins; expiry applied; fail-closed) - verifications table migration (scripts/002) with RLS + indexes - Unit tests for the aggregation rules; README + module wiring Closes Thalos-Infrastructure#74
c494e5f to
136f944
Compare
|
@josueazc could you please review the conflicts? Thank you |
…ation-api # Conflicts: # src/app.module.ts
|
Sure! I've reviewed the conflicts and they've been resolved. Thanks! |
Review — almost ready (a few blockers before merge)Thanks @josueazc — this is a strong fit for #74. The read/aggregation scope is clear, the response shape matches the issue, JWT is required, aggregation + fail-closed are well thought out, and the unit tests cover the important cases. Conflicts look resolved. Please address the following before we merge. Blockers
What looks good
Happy to re-review after the migration rename + authz tightening + a clear data-model note for KYC/KYB writers. |
…SSoT docs) - Rename migration 002_create_verifications.sql -> 004 to clear the number collision with kyb (002, main), kyc (Thalos-Infrastructure#80) and retry-queue (Thalos-Infrastructure#103) migrations. - Tighten authorization on the compliance endpoints (IDOR fix): reads now require the caller to be the subject (users), an admin, or an internal service. Adds JwtOrInternalSecretGuard (JWT or x-thalos-internal-secret) and VerificationService.assertCanRead; unit tests cover self/admin/internal/denied. - Document the single-source-of-truth model in the README: verifications is a read-only projection that KYC/KYB writers must upsert into, so Thalos-Infrastructure#71/Thalos-Infrastructure#72/Thalos-Infrastructure#75 don't invent another status shape.
Re-review — LGTMThanks @josueazc — Verified
CI: Test / Lint / Build / Format green (Vercel fail expected on this Nest repo). Able to merge. Maintainer checklist on merge
Optional follow-ups (non-blocking)
Approve / merge from my side. |
What & why
Implements a centralized Verification Status & Compliance API — the single source of truth for KYC (individual users) and KYB (organizations) compliance status across Thalos. Other services (Agreements, Reputation, Enterprise, future marketplace) can consume it without caring which identity provider produced the data.
Closes #74.
Scope is intentionally the read/aggregation side described in #74. The full provider abstraction with sessions/create/cancel (#71) and the integration test suite (#75) are sibling issues and out of scope here.
Endpoints
GET /v1/verification/user/:idGET /v1/verification/business/:idBoth always return 200 with a standardized payload — an unknown or unverified subject returns an
unverifiedresponse rather than a 404, so consumers always get the same shape.Standardized response
{ "subjectType": "user", "subjectId": "…uuid…", "isVerified": true, "status": "verified", // unverified | pending | verified | expired | rejected "level": "standard", // none | basic | standard | advanced "provider": "persona", // provider backing the effective verification (or null) "expiresAt": "2027-01-01T00:00:00.000Z", "lastUpdated": "2026-05-01T00:00:00.000Z" }Maps 1:1 to the requested fields: Is Verified? / Verification Level / Verification Expiration / Verification Provider / Last Updated.
Provider-agnostic aggregation
A subject can have several
verificationsrows (one per provider). The service reduces them to one effective status:verifiedrow past itsexpires_atcounts as expired (not verified).isVerifiedis true iff at least one row is currently-validverified.level/provider/expiresAtcome from the highest-level valid row (ties broken by most recent update).statussurfaces the most advanced state present (pending>expired>rejected>unverified).Changes
src/verification/— module, controller, service, types, unit testsscripts/002_create_verifications.sql—verificationstable (provider-agnostic) with RLS + indexes. Not yet applied — run against Supabase before hitting the endpoints.app.module.tswiring + README (Modules / Main routes tables)Design notes / decisions
:idis validated as a UUID (ParseUUIDPipe).enterpriseprofile;subject_idis stored generically (no hard FK) so a dedicated organizations entity can slot in later without a schema break.Testing
Unit tests cover the aggregation rules (no records, valid verified, verified-but-expired, highest-level-wins across providers, pending, DB-error fail-closed).