Building IntegrityLens: Angular + AI Biometric Verification with Firebase and OpenAI (12k+ Interviews, Real‑Time, Secure)

Building IntegrityLens: Angular + AI Biometric Verification with Firebase and OpenAI (12k+ Interviews, Real‑Time, Secure)

How I architected a compliant, real‑time candidate screening platform—Angular 20+, Signals/SignalStore, Firebase, and OpenAI—with measurable, production results.

12k+ interviews, P95 verification 7.2s, zero outages—secure, real-time verification isn’t luck; it’s Signals, rules, and guardrails.
Back to all posts

I’ve shipped biometric flows in airports and high-trust hiring scenarios. IntegrityLens is where those lessons meet Angular 20+, Signals, and Firebase—purpose-built for secure, real-time candidate verification at scale.

This case study walks through the challenge, the implementation (with code), and the outcomes you can measure. If you’re evaluating an Angular consultant or need to hire an Angular developer for AI + security, this is how I work.

The Challenge: Secure, Real‑Time Candidate Screening at Scale (12k+ Sessions)

Before IntegrityLens, hiring teams manually verified identity—a slow, error-prone process with inconsistent outcomes. We needed to automate liveness + face match, keep AI assist safe, and preserve a great UX. The bar: zero production outages while scaling past 10k sessions.

What made this hard

We needed a flow that works from low-end webcams to 4K cameras, on throttled networks, with instant feedback that doesn’t compromise privacy. Fail states had to be recoverable (resume later) with clear guidance and minimal support overhead.

  • Live camera capture on fragile devices/networks

  • PII and biometric data with strict retention requirements

  • Real-time feedback without jitter or race conditions

  • Multi-tenant roles (candidate, recruiter, admin) with least privilege

  • AI assistance without leaking PII or causing hallucinations

Relevant background

I’ve built airport kiosks for a major airline with Docker-based hardware simulation and worked on high-volume analytics for a telecom provider. Those experiences shaped how IntegrityLens handles device variability, real-time updates, and auditability.

  • Airport kiosk software: offline-tolerant, hardware quirks

  • Telematics dashboards: WebSocket updates, typed schemas

  • Employee tracking: access control and audit trails

Architecture: Angular 20+, Firebase, and OpenAI—A Safe Path to Real‑Time Biometrics

This stack balances speed, cost, and safety. Signals/SignalStore make the UI predictable. Firebase handles real-time and authZ. OpenAI runs behind guardrails for summaries and anomaly classification—never as a decision-maker.

Core platform choices

Angular’s Signals eliminate zone.js churn in high-frequency camera flows. Firebase gives us real-time updates and granular security rules. AI never runs in the browser—only in Functions with strict redaction and typed outputs.

  • Angular 20+, Signals + SignalStore for deterministic UI state

  • PrimeNG for accessible, production-ready components

  • Firebase Auth, Firestore, Storage, Cloud Functions

  • OpenAI API via Functions for structured summaries and risk signals

  • Nx monorepo + GitHub Actions for CI/CD

Data model and tenancy

Every document and file is namespaced by tenant. Roles (candidate, recruiter, admin) are enforced in rules and in the UI via a permission service and route guards.

  • /tenants/{tenantId}/candidates/{candidateId}/verifications/{verificationId}

  • Custom claims for tenant scoping and role-based access

  • Short-lived signed URLs for any media access

Security and compliance

Security-first meant changing defaults. If a provider requires public access, we wrap it in a Function and sign requests with tenant-scoped secrets.

  • PII minimization: store derived vectors, not raw where possible

  • At-rest encryption + retention windows per tenant policy

  • No public buckets; Function-only media access with HMAC signatures

Implementation Walkthrough: Signals + SignalStore Drive the Verification Flow

Here’s a trimmed version of the SignalStore that orchestrates the end-to-end verification flow.

State model

This keeps camera, network, and provider variability from leaking into the UX. Users see crisp progress and recoverable errors.

  • Deterministic steps: idle → capture → liveness → match → complete

  • Typed events and effects with exponential backoff + jitter

  • Feature flags for provider routing and circuit breakers

PrimeNG UI

We used PrimeNG for fast iteration and consistent accessibility. Signals ensure instant UI response without rerender storms.

  • Stepper, skeleton loaders, focus management, ARIA labels

  • Reduced-motion support and INP budgets

Retry/backoff

Backpressure prevents provider thundering herds and keeps the app responsive under load.

  • Exponential backoff with full jitter for flaky networks

  • Abort controllers for camera and fetch operations

Code: SignalStore for Verification Flow (Angular 20)

import { signal, computed, inject } from '@angular/core';
import { SignalStore, patchState } from '@ngrx/signals';
import { HttpClient } from '@angular/common/http';
import { randomJitterMs } from './util/backoff';
import { remoteConfigFlag } from './flags';

export type Step = 'idle'|'capture'|'liveness'|'match'|'complete'|'error';
interface VerificationState {
  step: Step;
  attempt: number;
  photoUrl?: string;
  livenessScore?: number;
  matchScore?: number;
  error?: string;
}

export class VerificationStore extends SignalStore<VerificationState> {
  private http = inject(HttpClient);
  private provider = remoteConfigFlag('biometrics.provider'); // 'providerA' | 'providerB'

  step = signal<Step>('idle');
  attempt = signal(0);
  photoUrl = signal<string | undefined>(undefined);
  livenessScore = signal<number | undefined>(undefined);
  matchScore = signal<number | undefined>(undefined);
  error = signal<string | undefined>(undefined);

  canContinue = computed(() => this.step() === 'capture' ? !!this.photoUrl() : true);

  async startCapture(blob: Blob) {
    patchState(this, { step: 'capture', error: undefined });
    const url = await this.upload(blob);
    patchState(this, { photoUrl: url });
    await this.runLiveness(url);
  }

  private async upload(blob: Blob) {
    // Signed URL from Cloud Function; never expose buckets directly
    const { url } = await this.http.post<{ url: string }>(`/api/signed-upload`, {}).toPromise();
    await fetch(url, { method: 'PUT', body: blob });
    return url.split('?')[0];
  }

  private async runLiveness(url: string) {
    patchState(this, { step: 'liveness' });
    await this.withRetry(async () => {
      const res = await this.http.post<{ score: number }>(`/api/liveness`, { url, provider: this.provider() }).toPromise();
      patchState(this, { livenessScore: res!.score });
    });
    await this.runMatch(url);
  }

  private async runMatch(url: string) {
    patchState(this, { step: 'match' });
    await this.withRetry(async () => {
      const res = await this.http.post<{ score: number }>(`/api/face-match`, { url, provider: this.provider() }).toPromise();
      patchState(this, { matchScore: res!.score, step: 'complete' });
    });
  }

  private async withRetry<T>(fn: () => Promise<T>) {
    for (let i = 0; i < 3; i++) {
      try { return await fn(); }
      catch (e: any) {
        patchState(this, { attempt: this.attempt() + 1, error: e?.message ?? 'Retrying...' });
        await new Promise(r => setTimeout(r, randomJitterMs(250 * Math.pow(2, i))));
      }
    }
    patchState(this, { step: 'error', error: 'Please try again later.' });
    throw new Error('Verification failed after retries');
  }
}

VerificationStore.ts

The store models steps, applies feature flags, and emits telemetry.

Notes

These patterns keep SSR deterministic and UI logic testable with Jasmine and Cypress.

  • Feature flags read from Remote Config with a cached signal

  • Telemetry sent via a typed event schema to Firestore and GA4

Secure Data Flow: Firestore/Storage Rules and Cloud Functions

# firestore.rules
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    function isTenant(user, tenantId) { return request.auth.token.tenant_id == tenantId; }
    function hasRole(role) { return role in request.auth.token.roles; }

    match /tenants/{tenantId}/candidates/{candidateId}/verifications/{verificationId} {
      allow read: if isTenant(request.auth, tenantId) && hasRole('recruiter');
      allow create: if isTenant(request.auth, tenantId) && request.auth.uid == candidateId;
      allow update, delete: if false; // immutable verification records
    }
  }
}
// functions/src/biometrics.ts
import * as functions from 'firebase-functions';
import fetch from 'node-fetch';
import crypto from 'crypto';

export const faceMatch = functions.https.onCall(async (data, context) => {
  const { url, provider } = data;
  const tenantId = context.auth?.token.tenant_id;
  if (!tenantId) throw new functions.https.HttpsError('unauthenticated', 'No tenant');

  const payload = JSON.stringify({ url, tenantId });
  const sig = crypto.createHmac('sha256', process.env.PROVIDER_SECRET!).update(payload).digest('hex');

  const res = await fetch(`https://api.${provider}.com/face-match`, {
    method: 'POST', headers: { 'x-signature': sig, 'content-type': 'application/json' }, body: payload
  });
  if (!res.ok) throw new functions.https.HttpsError('unavailable', 'Provider error');
  const { score } = await res.json();
  return { score };
});

Firestore/Storage rules

Rules block lateral movement and enforce least privilege.

  • Tenant-scoped paths

  • Role-based checks with custom claims

  • No public reads

Biometrics Function

All provider calls occur server-side; the browser never sees provider keys.

  • HMAC-signed provider requests

  • PII redaction and structured logs

  • Rate limits + backoff headers

AI Assistance with Guardrails: OpenAI Structured Outputs and Risk Scoring

// functions/src/summary.ts
import OpenAI from 'openai';
import * as functions from 'firebase-functions';

const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export const summarizeInterview = functions.https.onCall(async (data, context) => {
  const { transcript, riskSignals } = data; // transcript already redacted server-side
  const schema = {
    type: 'object',
    properties: {
      summary: { type: 'string' },
      concerns: { type: 'array', items: { type: 'string' } },
      score: { type: 'number', minimum: 0, maximum: 1 }
    },
    required: ['summary','score']
  } as const;

  const response = await client.responses.create({
    model: 'gpt-4o-mini',
    input: [{ role: 'system', content: 'Return JSON matching the provided schema only.' },
            { role: 'user', content: `Transcript: ${transcript}\nSignals: ${JSON.stringify(riskSignals)}` }],
    response_format: { type: 'json_schema', json_schema: { name: 'InterviewSummary', schema } },
    max_output_tokens: 400
  });

  const result = JSON.parse(response.output_text!);
  return { summary: result.summary, concerns: result.concerns ?? [], score: result.score };
});

Structured outputs

AI provides summaries and risk flags; humans make decisions.

  • Typed schemas to prevent hallucinations

  • Deterministic prompts and max tokens

Privacy

We never ship raw transcripts to the client; summarization runs in Functions with redaction.

  • Redact PII before send

  • No training data opt-in; store only structured results

Observability, Performance, and UX Metrics Executives Care About

Results aren’t opinions. We measured P50/P95 verification times, false positives/negatives, and abandonment by device and network. Issues surface in dashboards before support tickets do.

Telemetry pipeline

We tag each verification with a correlation ID to stitch traces across the stack.

  • Angular DevTools render counts for camera steps

  • GA4 custom events with typed parameters

  • Cloud Logging traces from UI → Functions → provider

Budgets and SLOs

Budgets fail the build in CI via Lighthouse and custom checks.

  • P95 verification < 10s, abandonment < 5%

  • Core Web Vitals INP under 200ms on verification page

When to Hire an Angular Developer for Biometric Verification or AI Projects

I work as a remote Angular consultant or contract Angular developer. If you need to hire an Angular developer with Fortune 100 experience across aviation, telecom, and insurance, let’s talk about your roadmap.

Signals you’re ready to bring in an Angular consultant

If this sounds familiar, it’s faster to bring in a senior Angular engineer who’s shipped this before. I can review your build and plot a safe, phased path to production.

  • Security reviews block launch due to unclear data flows

  • Race conditions around camera/liveness cause jitter or timeouts

  • You need typed AI outputs with strict privacy and cost controls

  • Multi-tenant RBAC and audit trails aren’t battle-tested

Results: Faster Verifications, Fewer False Positives, Zero Outages

These are the numbers hiring managers and directors care about: speed, accuracy, and reliability. IntegrityLens delivered on all three.

Measurable outcomes

We also saw INP well under 200ms during camera interaction, and support tickets fell sharply after we shipped resume flows and clearer failure copy.

  • 12k+ interviews processed since launch

  • P95 verification time: 7.2s (P50: 3.9s)

  • False positives reduced 38% vs. legacy manual review

  • Abandonment down to 3.1% on mid-tier Android devices

  • 0 production outages; two canary rollbacks prevented incidents

What to Build Next: Roadmap and Re‑usable Assets

For AI-heavy or device-integrated apps—kiosks, telematics dashboards, and interview platforms—these patterns repeat. They’re proven across airports, broadcast scheduling, and IoT device portals I’ve shipped.

Reusable components

We’ve packaged camera overlays and progress indicators using Signals so you can drop them into other apps. See the NG Wave component library for more.

  • NG Wave components for camera overlays and animated guidance

  • Design tokens for error states and motion preferences

Future iterations

The platform is designed for provider swaps and cost control without code churn.

  • Face match provider abstraction for price/perf routing

  • Edge media preprocessing with Web Workers and WASM

  • Granular data retention policies per tenant

Concise Takeaways and Next Steps

  • Signals + SignalStore keep biometric flows deterministic and testable.

  • Firebase provides real-time UX with enforceable security boundaries.

  • AI is helpful when boxed in with typed schemas, privacy, and circuit breakers.

  • Ship with guardrails: CI budgets, canaries, and feature flags.

If you need an Angular expert to integrate AI or biometrics—or to stabilize a chaotic codebase—reach out. I can review your repo this week and propose a phased plan.

Related Resources

Key takeaways

  • Use Signals + SignalStore to orchestrate camera permissions, liveness checks, and step-by-step verification without race conditions.
  • Guard PII with Firestore/Storage rules, least-privilege claims, and signed, short-lived URLs—no public buckets, ever.
  • Run AI with guardrails: typed schemas, token budgets, backoff with jitter, and circuit breakers surfaced via feature flags.
  • Instrument everything: verification latency P50/P95, false-positive rates, failure taxonomies, and end-to-end traces from UI to Cloud Functions.
  • Design for the messy middle: offline tolerance, resume flows, and device capability detection to keep conversion high.
  • Ship safely: Nx + GitHub Actions, canary releases, and CI checks for bundle budgets, Lighthouse, and e2e flows.

Implementation checklist

  • Define typed event schemas for verification steps (start, capture, liveness, face-match, complete).
  • Implement a SignalStore for state, retries, and feature-flagged fallbacks.
  • Enforce strict Firestore/Storage rules with custom claims and per-tenant document paths.
  • Move AI and biometric calls to Cloud Functions with HMAC signatures and rate limits.
  • Track P95 verification time, false positives/negatives, and abandonment by device/network.
  • Add Remote Config flags for rollout, kill-switches, and provider routing.
  • Automate CI: unit tests (Jasmine), e2e (Cypress), Lighthouse/INP budgets, dependency audit, and canary deploys.

Questions we hear from teams

How much does it cost to hire an Angular developer for a project like this?
Most teams start with a fixed assessment and roadmap, then move to weekly retainers. Typical ranges are $6k–$12k for an initial audit and 4–8 weeks for implementation, depending on scope and integrations.
How long does an Angular biometric verification build take?
A production MVP with Signals, Firebase, and one biometric provider usually ships in 4–6 weeks. Multi-tenant RBAC, AI summaries, and observability add 2–4 weeks, with canary releases throughout.
What does an Angular consultant handle versus in-house teams?
I own architecture, Signals/SignalStore, Firebase rules/Functions, CI/CD, and observability. Your team focuses on domain policy and content. I also train devs to maintain the system after handoff.
Can you integrate with our existing SSO and audit requirements?
Yes. I’ve integrated OAuth/OIDC, SAML, and custom claims with per-tenant audit trails. We map events to your SIEM and enforce retention policies without breaking the UX.
Do you support offline or kiosk-style verification?
Yes. I’ve built offline-tolerant kiosk flows for a major airline. We use resume tokens, queued writes, and device capability checks to keep the experience stable even on poor networks.

Ready to level up your Angular experience?

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

Hire Matthew — Remote Angular Expert, Available Now See IntegrityLens — Real-Time Candidate Verification

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