Identity and Trust

Consumer-owned authentication, device binding, and optional attestation

Application identity

Browser clients default to trust.browserPersistence: 'session': keys and certificates stay in memory, without probing IndexedDB CryptoKey storage. Anonymous spaces, rooms and tickets work in this mode. Reloading or creating a new client creates a new session identity; it does not replace or delete any previously remembered device.

Durable browser device enrollment (including device-backed authenticated rooms) requires an explicit choice before client creation:

const rtc = OpenRTC({
  apiKey,
  trust: { browserPersistence: 'persistent' },
});

Use this after your application offers a “Remember this browser” choice. Persistent mode uses non-extractable WebCrypto keys in IndexedDB. Apple WebKit may request its OS Keychain wrapping key, so this mode can display OS UI; the website does not receive access to saved passwords. Without the choice, durable enrollment fails before authentication/network requests instead of registering a new retained device on each reload. Native host providers keep their existing app-private storage and are not changed by this browser setting.

The choice is fixed for the client lifetime. Do not switch an active transport identity between session and persistent storage: start a fresh page/client before enabling durable mode. Storage denial in explicit persistent transport mode is an error, not permission to silently fall back to weaker storage.

OpenRTC does not own your login screen or user database. Provide a registered OIDC/JWKS assertion through AuthProvider:

const auth = {
  getAssertion: ({ forceRefresh }) => backend.openRtcAssertion({ forceRefresh }),
  // Subscribe to login, logout, account switch, or explicit revocation only.
  // Do not forward ordinary access-token refresh notifications.
  subscribe: (listener) => identity.onChange(listener),
};

const devices = await rtc.devices.start({ auth, autoConnect: 'online' });

Assertions must use the OpenRTC exchange audience, a registered HTTPS issuer/JWKS, supported signing algorithm, and bounded sub, iat, and exp claims. Standard OIDC assertions with a lifetime of up to one hour are accepted; OpenRTC still issues only a five-minute identity session. jti is recommended but optional: when the provider omits it, OpenRTC uses the signed assertion fingerprint as the replay fence. The SDK binds each exchange to its local device, so the same provider token may enroll separate devices once each without becoming a reusable session token. If the provider signs an openrtcDeviceId claim, it must match the SDK's requested device. OpenRTC also preserves a valid OIDC auth_time: ordinary token refresh retains the old value, while an interactive sign-in after device removal authorizes exactly one bounded re-enrollment through the existing enrollment transaction.

OpenRTC derives a stable app-scoped principal, enrolls the install's Ed25519 device key, and issues a scoped gateway grant. Identity refresh for the same principal updates the active state without reopening a healthy socket.

subscribe is an identity-epoch observer, not an OAuth-token observer. If an identity SDK reports both kinds of event, filter by the stable signed-in principal before calling the listener. Forwarding hourly Firebase ID-token or OAuth access-token refreshes as identity changes would intentionally retire the old security scope and can create avoidable assertion, grant, socket, presence, and metering work. OpenRTC requests a fresh consumer assertion only after a rejection or when a new identity/device certificate is actually required.

Anonymous capabilities

Spaces and ephemeral rooms may use capability access. They create no Firebase Auth user and no monthly-active-principal event:

const room = await rtc.rooms.join('public-lobby', {
  access: 'capability',
  membership: 'ephemeral',
});

OpenRTC still validates origin, device signature, nonce, replay, manifest policy, rate, and budget before creating live avenue state.

Managed attestation

Attestation is evaluated at enrollment, key rotation, recovery, or risk escalation—not as a connection heartbeat.

import { AppCheckProvider } from 'openrtc-trust-firebase-app-check';

const rtc = OpenRTC({
  apiKey,
  trust: {
    attestation: new AppCheckProvider({ appCheck }),
  },
});

Your app owns provider registration and native signing setup. Managed verification is an advanced portal capability and consumes usage credits. A consumer backend may instead validate evidence and issue a registered assertion.

Firebase App Check is reusable app-session evidence. It proves the registered application session, while OpenRTC separately binds enrollment to its nonce and per-install device-key signature. Apple App Attest and Play Integrity adapters provide request-challenge evidence. DeviceCheck is a lower-trust app-session fallback. OpenRTC derives this policy from the registered server provider; a client cannot upgrade its own trust level.

Normal access-token, App Check, and attestation refreshes are not OpenRTC connection timers. The SDK requests managed evidence only for enrollment, key rotation, recovery, or explicit risk escalation. A healthy avenue refreshes its one-hour OpenRTC grant in place without reopening its socket or rewriting presence.