Building IntegrityLens: Angular 20 + Signals, Firebase, and OpenAI for Real‑Time Biometric Verification (12k+ Interviews)

Building IntegrityLens: Angular 20 + Signals, Firebase, and OpenAI for Real‑Time Biometric Verification (12k+ Interviews)

How I architected a secure, real-time Angular 20+ verification flow—biometrics, AI scoring, and tenant isolation—running on Firebase with OpenAI APIs at scale.

Verification isn’t a dialog—it’s a protocol. Signals make it deterministic; Firebase makes it real-time; good rules make it safe.
Back to all posts

I’ve shipped biometric flows in high-stakes environments—airport kiosks, insurance telematics, and now IntegrityLens, my AI-powered verification and interview scoring platform. This case study breaks down how we built secure, real-time candidate screening on Angular 20 with Signals + SignalStore, Firebase, and OpenAI—now past 12k interviews.

If you’re looking to hire an Angular developer or an Angular consultant for secure AI verification, this is the architecture I’d bring to your team: deterministic state, strict isolation, streaming AI, and measurable UX.

The night we caught fraud at scale: shipping biometric verification without killing UX

Challenge

A late-night spike in flagged activity forced our hand: ship biometric verification without increasing drop-offs. I’ve been here before—airport kiosks, IoT device portals—where UX friction kills throughput. For IntegrityLens, the bar was higher: real identities, real interviews, and a clean audit trail.

  • Fraud attempts spiked as interview volume grew.

  • Verification had to be real-time and friendly to legit candidates.

  • Security, privacy, and auditability were non-negotiable.

Intervention

We built a signals-driven flow that streams updates from Firestore and Cloud Functions. The UI never polls; it reacts. Every event—liveness passed, match score, AI analysis—lands in a typed document that the UI renders immediately.

  • Angular 20 + Signals/SignalStore for deterministic UI.

  • Firebase for auth, data, rules, and real-time streams.

  • OpenAI for interview scoring; a liveness/match provider for biometrics via Functions.

Measurable result

The launch held up under peak loads with no production regressions. Fraud attempts became visible in near real time, and legit candidates sailed through.

  • 12k+ interviews processed.

  • 97% completion rate; median liveness 14s.

  • 99.95% uptime, sub-200ms state-to-UI propagation.

Why secure, real-time biometric verification matters for Angular 20+ teams

What leaders care about

If your app gates access to value—jobs, devices, payments—you need verification that’s fast for good users and unforgiving for bad actors. With Angular 20, Signals let us keep UX deterministic while Firebase gives us real-time, rules-backed data. OpenAI adds semantic scoring for interviews without building an ML team.

  • Security and compliance: tenant isolation, audit logs, PII lifecycle.

  • Throughput: fast verification without false positives.

  • Reliability: measurable SLOs, rollbacks, and CI gates.

Architecture: Angular 20 + Signals, SignalStore, Firebase, and OpenAI

Core stack

This is a full real-time stack: Firestore streams changes to the UI, Functions orchestrate AI and biometrics, and Storage handles media with signed URLs. Signals glue the UI together; SignalStore scopes state by tenant/session so we can recover from partial failures without reloading.

  • Angular 20, Signals + SignalStore, PrimeNG for forms/steps.

  • Firebase: Auth, Firestore, Functions, Storage, Hosting.

  • OpenAI for interview scoring; third-party liveness/match via Functions.

  • Nx monorepo, Cypress, GitHub Actions, Firebase Hosting previews.

Data model (simplified)

We keep a single session doc with an immutable event log. The UI renders from session.status and session.latestEvent, while analysts (and audits) read the full event history.

  • tenants/{tenantId}/candidates/{candidateId}/sessions/{sessionId}

  • sessions/{sessionId}/events/{eventId} for liveness/match/ai

  • sessions/{sessionId}/artifacts/photo.jpg, transcripts.json

Security boundary

Every request is scoped to a tenant; no cross-tenant reads. Functions re-check claims to avoid client tampering. We rotate signed URLs for Storage and scrub PII in logs.

  • Auth custom claims { tenantId, roles }

  • Firestore/Storage rules enforce tenant-only access

  • Functions validate claim ↔ path consistency

Signals-driven verification flow with Firestore streams

SignalStore for deterministic steps

Signals give us exact step boundaries, which matters when a camera drops or a user denies permissions. The store manages a single truth source for the UI, subscribers, and effects.

  • Orchestrates start → liveness → match → complete.

  • Streams Firestore doc; no polling.

  • Exponential retry on transient failures.

Code: store sketch

import { inject } from '@angular/core';
import { signalStore, withState, withMethods, patchState } from '@ngrx/signals';
import { Firestore, doc, onSnapshot } from 'firebase/firestore';
import { httpsCallable, Functions } from 'firebase/functions';

interface VerificationState {
  sessionId?: string;
  step: 'start'|'liveness'|'match'|'ai'|'complete'|'error';
  loading: boolean;
  error?: string;
  score?: number;
}

export const VerificationStore = signalStore(
  { providedIn: 'root' },
  withState<VerificationState>({ step: 'start', loading: false }),
  withMethods((store, firestore = inject(Firestore), functions = inject(Functions)) => ({
    async init(tenantId: string, candidateId: string) {
      patchState(store, { loading: true });
      const start = httpsCallable(functions, 'startVerification');
      const { data }: any = await start({ tenantId, candidateId });
      const sessionId = data.sessionId as string;
      patchState(store, { sessionId, step: 'liveness', loading: false });

      const ref = doc(firestore, `tenants/${tenantId}/candidates/${candidateId}/sessions/${sessionId}`);
      let unsub = onSnapshot(ref, (snap) => {
        const s = snap.data();
        if (!s) return;
        // Map backend status to UI step
        const map: any = { LIVENESS: 'liveness', MATCH: 'match', AI: 'ai', COMPLETE: 'complete', ERROR: 'error' };
        patchState(store, {
          step: map[s.status] ?? store.step(),
          score: s.ai?.score,
          error: s.error
        });
      }, (err) => {
        // simple exponential backoff
        let backoff = 500; let retries = 0;
        const retry = () => setTimeout(() => {
          if (retries++ > 5) {
            patchState(store, { step: 'error', error: 'Realtime connection lost' });
            return;
          }
          unsub = onSnapshot(ref, () => {}, retry);
          backoff = Math.min(backoff * 2, 8000);
        }, backoff);
        retry();
      });

      return () => unsub();
    },
  }))
);

Template fragment

<ng-container [ngSwitch]="store.step()">
  <app-liveness *ngSwitchCase="'liveness'" (passed)="/* write event */"></app-liveness>
  <app-match *ngSwitchCase="'match'" (matched)="/* write event */"></app-match>
  <app-ai *ngSwitchCase="'ai'"></app-ai>
  <p *ngSwitchCase="'complete'">Verification complete. Score: {{ store.score() }}</p>
  <app-error *ngSwitchCase="'error'" [message]="store.error()"></app-error>
  <app-spinner *ngSwitchDefault></app-spinner>
</ng-container>

Streaming AI scoring with OpenAI and Cloud Functions

Approach

We don’t block the UI waiting for final AI output. Instead, Functions stream deltas to Firestore. Candidates see progress; reviewers see partial insights without refreshing.

  • Transcripts saved to Firestore trigger scoring.

  • OpenAI streamed token-by-token; partial deltas written back.

  • Angular renders partials immediately via Signals.

Code: Firestore-triggered streaming

// functions/src/scoreInterview.ts
import { onDocumentCreated } from 'firebase-functions/v2/firestore';
import { getFirestore } from 'firebase-admin/firestore';
import OpenAI from 'openai';

const openai = new OpenAI();

export const scoreInterview = onDocumentCreated(
  'tenants/{tenantId}/candidates/{candidateId}/sessions/{sessionId}/transcripts/{tId}',
  async (event) => {
    const db = getFirestore();
    const { tenantId, candidateId, sessionId } = event.params as any;
    const transcript = event.data?.data()?.text as string;
    const sessRef = db.doc(`tenants/${tenantId}/candidates/${candidateId}/sessions/${sessionId}`);

    const stream = await openai.chat.completions.create({
      model: 'gpt-4o-mini',
      stream: true,
      messages: [
        { role: 'system', content: 'Score answers 0-100 with rationale. JSON only.' },
        { role: 'user', content: transcript }
      ]
    });

    let buffer = '';
    for await (const chunk of stream) {
      const delta = chunk.choices?.[0]?.delta?.content ?? '';
      buffer += delta;
      await sessRef.set({ ai: { partial: buffer.substring(0, 5000), status: 'STREAMING' } }, { merge: true });
    }

    // finalize
    const json = JSON.parse(buffer);
    await sessRef.set({ ai: { score: json.score, rationale: json.rationale, status: 'DONE' }, status: 'AI' }, { merge: true });
  }
);

Tenant isolation with Firebase Security Rules

Rules strategy

Security isn’t a checkbox; it’s the backbone. We treated every read/write as a potential lateral movement attempt. Here’s the rules template we ship with.

  • Auth custom claims carry tenantId and roles.

  • Paths always include tenantId; Functions revalidate.

  • PII: short-lived URLs, redact logs, TTL on raw artifacts.

Code: Firestore rules (excerpt)

// firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{db}/documents {
    function isTenant() { return request.auth != null && request.auth.token.tenantId != null; }
    function isTenantPath(tenantId) { return request.auth.token.tenantId == tenantId; }
    function hasRole(role) { return request.auth.token.roles != null && request.auth.token.roles.hasAny([role, 'admin']); }

    match /tenants/{tenantId}/{document=**} {
      allow read: if isTenant() && isTenantPath(tenantId) && hasRole('viewer');
      allow write: if isTenant() && isTenantPath(tenantId) && hasRole('editor');
    }
  }
}

Storage rules (pattern)

// storage.rules
rules_version = '2';
service firebase.storage {
  match /b/{bucket}/o {
    match /tenants/{tenantId}/sessions/{sessionId}/{allPaths=**} {
      allow read, write: if request.auth != null && request.auth.token.tenantId == tenantId;
    }
  }
}

Instrumentation, telemetry, and UX metrics

What we measured

We used Angular DevTools, Lighthouse CI, and GA4 events. Firebase Logs/BigQuery caught long-tail errors and correlated them with device/region. PrimeNG components carried accessible states and tokens for density/typography so performance wasn’t at odds with polish.

  • Step latencies (liveness, match, AI).

  • Completion, re-verify, and abandonment rates.

  • INP, TBT, and SSR hydration stability where applicable.

Code: GA4 event

// track step timing
logEvent(analytics, 'verify_step', {
  tenant_id: tenantId,
  session_id: sessionId,
  step,
  ms: performance.now() - t0
});

Results at scale: 12k+ interviews and counting

Numbers that matter

We shipped the biometric release without production fires. Fraud spikes showed up as time-series anomalies in BigQuery. Reviewers got partial AI insights as they arrived, cutting triage time while preserving candidate trust.

  • 12k+ interviews processed across tenants.

  • 97% completion, 1.8% re-verify, median liveness 14s.

  • Sub-200ms Firestore→UI propagation on average; 99.95% uptime.

When to hire an Angular developer for secure AI verification

Good moments to bring in an Angular consultant

If you need a remote Angular developer with Fortune 100 experience to stand up a secure, real-time verification flow—or to stabilize a vibe-coded one—this is where I help. From Signals migration to Firebase rules and streaming OpenAI, we’ll get you measurable outcomes fast.

  • You’re handling PII and need tenant isolation that passes audit.

  • Your team struggles with real-time state and race conditions.

  • You want AI scoring/biometrics without destabilizing UX.

Key takeaways and next steps

Recap

The pattern generalizes to kiosks, telematics dashboards, and media review tools: typed events, real-time updates, strict isolation, and instrumentation. If your roadmap includes AI + realtime + compliance, we should talk.

  • Signals + SignalStore kept verification deterministic and resilient.

  • Firebase gave us real-time, rules-backed data with strong tenancy.

  • OpenAI streamed insights safely through Cloud Functions.

Related Resources

Key takeaways

  • Signals + SignalStore gave us deterministic, real-time UI state for biometric steps without zone.js complexity.
  • Tenant isolation used Firebase Auth custom claims, Firestore/Storage rules, and scoped IDs to keep data locked per client.
  • OpenAI scoring streamed through Cloud Functions to Firestore, updating Angular UI live with partial results.
  • Typed event schemas and exponential retry logic stabilized flaky networks and reduced verification drop-offs.
  • Observability—GA4, Firebase Logs, and BigQuery—surfaced bottlenecks and fraud trends for 12k+ interviews.
  • Result: 97% completion rate, median liveness 14s, sub-200ms UI updates, 99.95% uptime across releases.

Implementation checklist

  • Define a typed verification session schema and event model before writing UI.
  • Use Signals + SignalStore for step orchestration and error boundaries.
  • Implement Firebase custom claims and rule templates for tenant isolation.
  • Stream AI scoring through Cloud Functions with token-by-token updates.
  • Instrument each step with GA4, Firebase Logs, and latency percentiles.
  • Add exponential backoff, resumable uploads, and feature flags for rollout.

Questions we hear from teams

How much does it cost to hire an Angular developer for a secure AI verification project?
Most teams start with a 2–4 week engagement to de-risk architecture and deliver a working vertical slice. Fixed-price pilots are common; longer builds move to weekly retainers. Book a discovery call to scope accurately.
How long does it take to build biometric verification in Angular with Firebase and OpenAI?
A pilot (single-tenant, end-to-end flow) typically ships in 3–5 weeks. Multi-tenant rollout with audits, analytics, and CI gates usually lands in 6–10 weeks depending on integrations and compliance needs.
What does an Angular consultant do on this kind of project?
Define schemas and flows, implement Signals/SignalStore, set up Firebase Auth/Rules/Functions, wire AI streaming, instrument metrics, and create rollout plans with feature flags, canaries, and rollbacks.
Is this approach compliant for PII and biometrics?
We minimize retained PII, use tenant-scoped Storage with short-lived URLs, redact logs, and apply TTLs. Final compliance depends on your policies and region; I can align architecture with your legal and security teams.
Can you rescue an existing vibe-coded Angular app with broken real-time state?
Yes. I stabilize codebases using SignalStore, strict TypeScript, telemetry, and Firebase rules. See gitPlumbers for code modernization services and rescue playbooks.

Ready to level up your Angular experience?

Let AngularUX review your Signals roadmap, design system, or SSR deployment plan.

Hire Matthew – Remote Angular Expert for Secure AI + Realtime See IntegrityLens – AI Verification + Interview Scoring

NG Wave

Angular Component Library

A comprehensive collection of 110+ animated, interactive, and customizable Angular components. Converted from React Bits with full feature parity, built with Angular Signals, GSAP animations, and Three.js for stunning visual effects.

Explore Components
NG Wave Component Library

Related resources