Skip to content

Architecture

This is a high level overview of the sequence of events that happens while using Liquid Auth. See the Getting Started section for more detailed information on each step. Diagrams are generated using Mermaid.

Authentication

A user can link their device to a website by scanning a QR code. The website will subscribe to a WebSocket channel to receive the link status. The wallet will scan the QR code and send a FIDO2 PublicKeyCredential to the server. The server will validate the FIDO2 credential and send a response to the wallet and website.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientSubscribe to 'wss:link'Display QR Connect Request IDScan QR CodeGet Challenge/OptionsPOST FIDO2 Credential + Liquid Auth ExtensionValidate SignaturesHTTPOnly SessionOk Response + HTTPOnly SessionEmit to `wss:link` client

Signaling

The website and wallet can subscribe to an isolated WebSocket channel to broker Session Description answers and offers. ICE Candidates are discovered when any peer has both an offer and answer.

Signaling is keyed on the requestId rather than the wallet address. When a client subscribes to wss:link, and when a wallet authenticates against a requestId, the server joins that socket to the requestId room. Descriptions and candidates are then brokered to that room, so negotiation works before the peer has authenticated and no longer depends on the wallet address.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientSubscribe to 'wss:offer-description'Subscribe to 'wss:offer-candidate'Subscribe to 'wss:answer-description'Subscribe to 'wss:answer-candidate'

Offer

Offers are created by a peer and sent through the signaling service. A client with an offer will listen for an answer description. Answers are only emitted in response to an offer. Offer clients are responsible for creating the Data Channel.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientOn answer-description, set Remote SDPOn answer-candidate, add ICE CandidateCreate Peer Offer & DataChannelEmit `wss:offer-description`Emit `wss:offer-candidate`

Answer

An Answer is created by a peer in response to an offer. The answer description and candidates are emitted to the signaling service.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientOn offer-description, set Remote SDP and create AnswerOn offer-candidate, add ICE CandidateEmit `wss:answer-description`Emit `wss:answer-candidate`

Data Channel

Once an Offer and Answer have been exchanged, a Data Channel will be emitted to the peer who created the answer. This channel is used to send messages between the website and wallet in real-time over the established P2P connection.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientEmit DataChannelOn Message, Handle MessageOn DataChannel, listen for MessagesEmit MessagesEmit Messages

Presence

Whenever a socket joins or leaves a requestId room the server broadcasts a wss:presence event to that room. Peers use it to decide whether the other party is available before attempting to (re)negotiate, and only negotiate once both peers are present (deviceCount >= 2).

deviceCount counts distinct devices — sockets are collapsed by their session id, so a device that briefly owns more than one socket (e.g. a lingering socket plus a fresh reconnect) is only counted once. online is true when at least one device is present. GET /auth/session reports the same live deviceCount.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientNegotiate only when deviceCount ≥ 2Connect / authenticate for `requestId`Join `requestId` room, count distinct devicesEmit `wss:presence` { requestId, deviceCount, online }Emit `wss:presence` { requestId, deviceCount, online }

Reconnection

Because both peers persist the requestId and the wallet retains a valid session, a dropped P2P connection is renegotiated over the existing socket without a fresh passkey prompt. The server re-announces auth when a bound wallet reconnects (or when the peer links while the wallet is already present), and both sides re-run the offer/answer exchange in the requestId room.

Offer ClientServerAnswer ClientOffer ClientServerAnswer ClientPreviously paired (share requestId + valid session)Both present — renegotiate and re-establish the Data ChannelConnection droppedEmit `wss:presence` { deviceCount: 1, online: true }Peer offline — tear down transport, keep socket & waitReconnect socket (no passkey prompt)Re-join `requestId` room, re-announce `auth`Emit `wss:presence` { deviceCount: 2, online: true }