TL;DR
- Deepfakes broke the old assumption that seeing a face on a call proves who's on it. Encryption and account logins were never designed to check that.
- A video call has four separate security layers: transport encryption (DTLS), media encryption (E2EE/SFrame), account authorization (tokens and roles), and human verification. Only the last one checks who's actually on camera.
- Stream covers transport encryption today and is adding native E2EE (SFrame) to the React SDK. Verifying the person on screen stays the developer's responsibility.
- Zoom and Microsoft shipped identity verification into mainstream video products in 2026. The question for developers is which of their own call types need the same guarantee.
In January 2024, a finance worker at the engineering firm Arup joined a video call with his company's CFO and several colleagues. The request was unusual: a series of confidential transfers, authorized on the spot. But the people asking were familiar faces, live on camera. He made 15 transfers totaling HK$200 million (about US$25.6 million) to five bank accounts.
Everyone else on that call was a deepfake, assembled from publicly available footage and voice samples.
Nothing about the session was compromised in the traditional sense. The connection was encrypted. The meeting was real. The attack worked because every security control involved verified the session, and none of them verified the humans in it.
This piece walks through what a video call actually secures today, where the gaps are, and what closing them looks like in practice.
Why is Human Verification Arriving Now?
Like they say in "All The President's Men," always follow the money. The costs explain the urgency. Deloitte's Center for Financial Services estimates that AI-enabled fraud losses in the US could grow from $12.3 billion in 2023 to $40 billion by 2027.
We can think about AI-enabled fraud along two axes:
- Quantity. Generative AI collapses the cost of volume. Scams that once needed a human to write every message, business email compromise being the classic case, can now run against thousands of targets at once, each message fluent, personalized, and free of the tells that used to give phishing away.
- Quality. The same models collapse the cost of fidelity. Producing a convincing human face in real time used to require resources far beyond a typical fraud operation, and now it runs on cheap, accessible tooling.
Volume is a problem the industry already has defenses for: spam filters, rate limits, anomaly detection, all tuned across years of email and payment fraud. Fidelity has nothing comparable yet.
Two years after the Arup call, that gap is reshaping product roadmaps. In April 2026, Zoom announced a partnership with Tools for Humanity to integrate World ID Deep Face into Zoom Meetings, verifying in real time that participants are actual humans. Verified attendees get a "Verified Human" badge on their video tile.
Microsoft has been moving in the same direction with Entra Verified ID, and its identity team frames the problem plainly: existing systems verify credentials, sessions, and devices, not the person behind them.
The two efforts sound similar but make two different promises, and the distinction matters when you're deciding what your product actually needs. World ID is proof of humanity. It confirms that a real, unique human is present without revealing who they are. Microsoft Entra Verified ID sits closer to traditional identity proofing. Its Face Check flow compares a real-time selfie against the photo from a government ID and shares only the match result with the relying application.
| World ID Deep Face | Entra Verified ID + Face Check | |
|---|---|---|
| Proves | A real, unique human is present | A specific named person matches their ID |
| How | Orb enrollment, then an on-device match against the live stream | Live selfie matched against a government ID photo |
| What's shared | An attestation and a badge; no personal data | Only the match result |
| Fits | Bot resistance, anonymous-but-human spaces | Approvals, compliance, regulated workflows |
Different products need different guarantees. A marketplace fighting bot armies needs proof of human. A payroll approval flow needs proof of person. A telehealth platform probably needs both, plus a record that verification happened. Knowing which guarantee your high-stakes moments require shapes every downstream build decision.
What Can a Video Call Verify?
There are four things a video call can verify: the connection, the media, the account, and the person. Each has its own control, each control answers exactly one question, and none of them covers for the others. That sounds obvious written down. In practice, the four get collapsed into one word, "secure," which is how a company ends up wiring $25 million to a face on a screen.
| Control | The question it answers | What still gets through |
|---|---|---|
| DTLS 1.3 transport encryption | Is the connection private and untampered? | Anything a legitimate participant sends |
| End-to-end media encryption (SFrame) | Can servers read the media? | Synthetic media from a real participant |
| Tokens, roles, permissions | Is this account allowed in, and what can it do? | A deepfake behind valid credentials |
| Human verification | Is a real person, or the right person, on camera? | Injection attacks, unless the check is bound to the live stream |
The first three check the connection, the media, and the account. None of them looks at the person, so their gaps all reduce to the same case: a fake face in a legitimate session. The fourth control closes exactly that gap, and until this year, no mainstream video product shipped it.
The Connection: Transport Encryption Stops at the Media Server
Transport encryption is the control that every video product already ships. Its job is to keep anyone sitting on the network path from reading or tampering with your packets, whether that's a coffee shop router, an ISP, or a compromised hop somewhere in the middle.
In WebRTC, that means DTLS (Datagram Transport Layer Security), because media travels over UDP and TLS assumes a reliable, ordered connection that UDP doesn't provide. DTLS is TLS reworked for packets that arrive late, out of order, or not at all. It produces the keys SRTP uses to encrypt the audio and video, and it encrypts data channels directly.
The current version is DTLS 1.3, and it's the reasonable minimum to ask of any video infrastructure in 2026. Plenty of deployments are still running 1.2. If you want to know if your stack is running it, the WebRTC stats API reports it directly:
12345678// pc is your RTCPeerConnection const stats = await pc.getStats(); stats.forEach((report) => { if (report.type === "transport") { // tlsVersion is wire-format hex: "FEFD" is DTLS 1.2, "FEFC" is DTLS 1.3 console.log(report.tlsVersion, report.dtlsCipher, report.srtpCipher); } });
If that prints FEFD, you're on 1.2. If it prints FEFC, it's 1.3. If you're using Stream, you also have access to the WebRTC stats API through the useCallStatsReport() hook:
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253import { useEffect } from "react"; import { useCallStateHooks } from "@stream-io/video-react-sdk"; // Maps the hex `tlsVersion` reported in RTCTransportStats to a readable name. const DTLS_VERSIONS: Record<string, string> = { FEFD: "DTLS 1.2", FEFC: "DTLS 1.3", }; const readTransports = (stats, leg) => { const out = []; stats?.forEach((r) => { if (r.type === "transport") { out.push({ leg, dtlsState: r.dtlsState, tlsVersion: r.tlsVersion, dtlsCipher: r.dtlsCipher, srtpCipher: r.srtpCipher, }); } }); return out; }; export const DtlsCheck = () => { const { useCallStatsReport } = useCallStateHooks(); const report = useCallStatsReport(); useEffect(() => { if (!report) return; const transports = [ ...readTransports(report.publisherRawStats, "publisher"), ...readTransports(report.subscriberRawStats, "subscriber"), ]; transports.forEach((t) => { // Until the handshake completes, tlsVersion/ciphers are undefined. if (t.dtlsState !== "connected" || !t.tlsVersion) { console.log(`[dtls:${t.leg}] handshaking… (state=${t.dtlsState})`); return; } console.log( `[dtls:${t.leg}] ${DTLS_VERSIONS[t.tlsVersion] ?? t.tlsVersion}`, `| dtlsCipher=${t.dtlsCipher}`, `| srtpCipher=${t.srtpCipher}`, ); }); }, [report]); return null; };
Here's the catch.
Almost every production video product routes calls through an SFU (selective forwarding unit) because peer-to-peer meshes stop scaling past a handful of participants. And in an SFU architecture, transport encryption is hop-by-hop: the server terminates the encrypted connection, handles your media as plaintext frames, and re-encrypts on the way out to each recipient.
The server needs that access to route packets, and features like recording depend on it, so this is by design. But it means transport encryption alone never gave you end-to-end anything in a group call. It secures the path to the server and the path from it, and the server itself sees everything.
Stream runs DTLS 1.3 across the whole media path, including the hops between its own media servers when a call cascades across multiple edge locations.
The Media: Encryption the Server Can't Read
If the SFU sees plaintext, the fix is to encrypt the media itself in a way the SFU can route without reading it. That's what SFrame does. Each media frame is encrypted and authenticated end-to-end between participants, while the packet metadata the SFU needs for forwarding decisions stays visible. It was designed for server-routed conferencing from the start, and it's the approach the industry has converged on.
Browser support is through WebRTC Encoded Transforms. This API lets you intercept and modify encoded frames before they're sent. You can use this to roll your own media encryption, but doing so can fall apart for a few reasons:
- The SFU has to be in on it. A frame carries two separable things: the content (pixels, audio) and the structure (is this a keyframe, which quality layer, what it depends on). For routing, the server only ever needs the structure. E2EE splits the two: content gets encrypted, and structure moves to cleartext fields the server can still read. Clients and servers have to agree on that split, which is what SFrame standardizes.
- Keys need their own secure channel. Everyone on the call has to hold the same frame keys while the server holds none of them, and the set of people changes mid-call: someone joins, someone leaves, and the keys rotate so the newcomer can't read the past and the leaver can't read the future. There is an entire protocol for this problem, MLS, and it's not a gap you can vibe-code through.
- Every server-side media feature breaks. Recording, transcription, captions, AI summaries: they all depend on the server decoding your media, and a server that can't read frames can't process them. Zoom and Teams both ship E2EE and publish the same kind of trade-off list. Zoom's E2EE mode turns off cloud recording, live transcription, AI Companion features, streaming, whiteboards, and polling. Teams E2EE drops recording, captions, and transcription, and limits which clients can join.
Those tradeoffs are real product decisions: which call types get E2EE, what users are told about the features they lose, how compliance recording works when regulation requires it. They get made at the platform level, in coordination between the SDKs and the media servers. Bolting frame encryption onto app code skips the coordination and inherits all the breakage.
Stream is adding native end-to-end encryption to the React SDK in Q3 2026, with other SDKs to follow, built on the SFrame approach so it works with Stream's SFU and cascading architecture rather than around it. Stream has already made the public argument for this direction: SFrame is what makes E2EE viable for group calls without giving up the scalability of selective forwarding.
The Account: Tokens and Roles You Can Tighten Today
When building video, this is where weaknesses are usually self-inflicted. Stream won't admit anyone to a call without a JWT your server signed, and the role attached to that token decides what the account can do once it's in: join, publish, record, moderate. Call-scoped tokens handle narrow cases, such as granting a guest access to a specific call.
All of it is only as strict as you configure it. For anything sensitive:
- Generate tokens server-side with short expirations. Tokens are signed with your API secret, and anyone holding that secret can mint a valid identity for any user in your app, so it never touches a client. A long-lived token is a bearer credential waiting to leak.
- Keep default roles minimal. Grant join and publish, then add capabilities per role on purpose. It's much easier to widen a narrow role than to walk back a broad one.
- Don't ship permissive call types. Stream's development call type has everything enabled by design to keep friction low while you prototype. It has no place in a production flow that touches money or health data.
- Prefer call-level grants over app-wide ones. A support agent who needs recording on one call type doesn't need recording rights across all call types.
A deepfake defense means very little if anonymous users can wander into sensitive calls because a call type is shipped wide open. That's the full extent of it, though. Token auth verifies an account and authorizes what it can do. It has no opinion about the face on screen.
The Person: The Gap No SDK Closes For You
A user signs in and gets a valid token from your server. Their device is real. The call is end-to-end encrypted with keys only the participants hold.
And the "camera" the browser is capturing from is a virtual camera device, playing a real-time deepfake.
Nothing in the stack objects. The transport encrypts it, SFrame encrypts it, the token checks out, and the encryption even works against you here: no intermediary can inspect the stream. Every control below this one is doing exactly what it was designed to do.
Stream verifies the session, not the human, and so does every other video infrastructure provider today. A valid token tells you your app admitted the right account. It can't tell you the webcam feed belongs to the person who owns that account, that the feed is live, or that a virtual camera isn't sitting in the capture path. No SDK flag changes that, whatever the marketing around "deepfake protection" implies.
Zoom's implementation shows what closing the gap looks like in practice, and it breaks down into five pieces:
- An enrolled reference. Users verify once at an Orb, and that enrollment becomes the reference for every subsequent check.
- A gate at entry. With a Deep Face Waiting Room on, nobody gets into the meeting until they've verified against it.
- A check at the moment of decision. Any participant can trigger re-verification of another participant mid-meeting when a request seems off.
- A check bound to the live stream. What gets matched is the video Zoom is actually transmitting on the person's own device, so there's no way to verify with one camera while streaming from another.
- A visible result. Verified participants carry a badge on their tile that everyone else can see.
You can build every part of this:
- Your enrolled reference. Decide what identity you trust: an IdP account with MFA at the low end (it proves the account, not the face), a verifiable credential like Entra Verified ID, or a document-plus-selfie enrollment with an identity verification vendor at the high end.
- Your gate at entry. Your server already issues the call token, so the gate is the token endpoint: for sensitive call types, no verification, no token.
- Your check at the moment of decision. Before the wire transfer or the account recovery, force a fresh challenge: a step-up IdP login (SSO, passkey, MFA), a liveness check, or, for irreversible actions, out-of-band confirmation on a second channel. The Arup attack dies here.
- Your check bound to the stream. This is the piece that's hardest to buy today. Most identity verification products capture in their own flow, separate from the call. A virtual camera can feed that flow the same way it feeds yours using synthetic frames written straight into the pipeline. So a liveness check that only sees what the "camera" shows is checking the deepfake itself.
- Your visible result. Surface verification state on the participant in your UI, and write it to an audit log, because "we verified them at 2:14 pm" is the record that matters afterward.
Four of the five are buildable today from parts that already exist. The check that would have caught the Arup call is the out-of-band step in number three, and that's ordinary engineering.
Number four, binding the check to the stream itself, is the piece the market hasn't productized yet; Zoom shipping it is the clearest signal the gap is temporary. And the asymmetry runs in your favor. An attacker has to get past every check you put in the path, while you only need one of them to fire.
What to Build, By Stakes
None of this needs deciding product-wide. The controls sort by workflow, and most products end up running all three tiers side by side:
| Call type | What it needs |
|---|---|
| Every call | Encrypted transport (DTLS), server-issued short-lived tokens, minimal roles |
| Sensitive content (health, legal, HR) | Add end-to-end media encryption and accept the feature tradeoffs |
| High-stakes decisions (payments, approvals, account recovery) | Add human verification at the moment of action |
The first two rows are becoming the platform's job. Stream covers the first today, and the second arrives with native E2EE in the React SDK in Q3. The third stays with your application, and it should, because an infrastructure provider can't know that one particular button in one particular call authorizes a wire transfer.
That third row is also getting easier to fill. Zoom shipped proof-of-human verification into meetings this year, and Microsoft is certifying identity-verification partners for Entra Verified ID. What was an exotic enterprise add-on eighteen months ago is turning into a standard part of the stack.
The Arup employee did everything his tools asked of him. He joined a real meeting through real software and trusted the encrypted, authenticated faces on screen. Every control in that call did its job; none of them was pointed at the people. That's the part that finally changed this year. Deciding which of your calls need it is the part that's yours.

