wayray/docs/ai/adr/013-smartphone-proximity-token.md
Till Wegmueller 7db68df21f
Add ADR-013: Smartphone as BLE proximity token
Phone acts as wireless smart card -- walk up to terminal, session
appears; walk away, session suspends. No insertion, works from pocket.

- BLE beacon with encrypted rotating session token
- RSSI-based proximity detection with configurable thresholds
- Anti-flapping timers: T_attach (2s) and T_detach (10s)
- Security: HMAC timestamps, token rotation, optional NFC tap
- Companion app (Android/iOS): one-time OIDC setup, background BLE
- Implements same TokenProvider trait as smart cards (ADR-004)
- NFC as explicit complement, WiFi/mDNS as software fallback
2026-03-28 23:25:54 +01:00

9.1 KiB

ADR-013: Smartphone as Proximity Token

Status

Accepted

Context

SunRay's session mobility was driven by smart card insertion/removal. WayRay supports pluggable tokens (ADR-004). A smartphone is the one device users always carry. If the phone can act as a proximity token, we get automatic session follow without any explicit action -- walk up to a terminal, your desktop appears; walk away, it suspends.

This maps directly to SunRay's smart card semantics:

  • Smart card insert → phone enters proximity range
  • Smart card remove → phone leaves proximity range

But better: no physical insertion, works from your pocket.

Technology Options

The phone runs a small companion app that broadcasts a BLE advertisement containing a session token. The WayRay client has a BLE receiver that detects nearby phones.

How BLE beacons work:

  • Phone advertises a BLE beacon with a service UUID specific to WayRay
  • The advertisement payload contains an encrypted session token (≤31 bytes in legacy advertising, ≤255 bytes in extended)
  • The client scans for WayRay beacons and reads the token
  • RSSI (signal strength) determines proximity -- configurable threshold
  • When RSSI drops below threshold (user walked away), trigger disconnect

Advantages:

  • Always-on: phone advertises in background, no user action needed
  • Works from pocket (no need to pull phone out)
  • Range tunable via RSSI threshold (1-10 meters typical)
  • Low power: BLE advertising uses ~1-5% battery per day
  • Universal: every modern smartphone has BLE
  • Works through walls at close range (meeting rooms)

BLE Token Flow:

Phone (companion app):
  1. User authenticates in app once (OIDC, biometric, etc.)
  2. App receives signed session token from IdP/server
  3. App begins BLE advertising:
     Service UUID: WayRay-specific
     Payload: encrypted(token_id + timestamp + HMAC)
  4. Rotates payload periodically (replay prevention)

WayRay Client (BLE scanner):
  1. Continuously scans for WayRay service UUID
  2. Detects beacon → reads token → validates HMAC + timestamp
  3. If new token in range: trigger session attach
  4. If token leaves range (RSSI below threshold for N seconds):
     trigger session detach
  5. If multiple tokens: nearest (highest RSSI) wins

NFC -- Complementary

Phone tap on NFC reader for explicit authentication:

  • Quick deliberate action (tap to connect)
  • Works as fallback when BLE is disabled
  • Can trigger initial token provisioning
  • Very short range (~4cm) -- no proximity tracking

UWB (Ultra-Wideband) -- Future

Precise distance measurement (10cm accuracy):

  • iPhone U1/U2 chip, some Samsung/Google phones
  • Could enable "desk assignment" -- know exactly which terminal you're closest to
  • Not yet universal enough to depend on
  • Consider as enhancement when hardware penetration increases

WiFi Proximity (mDNS) -- Fallback

Phone app announces presence on local network:

  • Works without BLE hardware on client
  • Coarse proximity (same VLAN/subnet)
  • Higher latency (mDNS discovery takes seconds)
  • Can't distinguish between terminals in the same room
  • Useful as a fallback when BLE isn't available

Decision

BLE as primary proximity mechanism, NFC as explicit-action complement, WiFi/mDNS as software-only fallback.

Proximity State Machine

                          BLE beacon detected
                          (RSSI > threshold)
    [No Phone] ─────────────────────────────────> [Detected]
                                                      │
                                                      │ stable for
                                                      │ T_attach seconds
                                                      v
                    ┌──────────────────────────── [Attached]
                    │                                  │
                    │  BLE beacon returns              │ RSSI < threshold
                    │  (RSSI > threshold)              │ for T_detach seconds
                    │                                  v
                    └──────────────────────────── [Detaching]
                                                      │
                                                      │ timeout expires
                                                      v
                                                 [Detached]
                                                      │
                                                      │ (session suspends)
                                                      v
                                                 [No Phone]

Timers prevent flapping:

  • T_attach: delay before attaching (default: 2 seconds). Prevents drive-by session grabs when walking past a terminal.
  • T_detach: delay before detaching (default: 10 seconds). Prevents session drop when phone briefly loses signal (body shielding, phone rotates in pocket).
  • Both configurable per-deployment.

Security Considerations

Replay attacks: Token payload includes a timestamp and HMAC. Client rejects tokens older than N seconds. Phone rotates payload every 30 seconds.

Relay attacks: An attacker could relay the BLE signal from a distant phone to a nearby client. Mitigations:

  • Token payload includes a challenge-response nonce (requires phone app to respond)
  • RSSI-based distance bounding (relayed signals have abnormal RSSI patterns)
  • Optional: require NFC tap for initial session attachment, BLE only for persistence
  • For high-security deployments: disable BLE proximity, use smart card only

Token theft: If someone clones the BLE advertisement, they get the session token. Mitigations:

  • Token rotation (new token every 30s, phone signs each one)
  • Mutual authentication: client challenges phone via BLE GATT connection
  • Binding token to phone's hardware attestation key (Android SafetyNet / iOS DeviceCheck)

Multi-phone scenarios: When multiple phones are near a terminal:

  • Highest RSSI wins (closest phone)
  • If tie: first-arrived wins
  • Explicit NFC tap overrides BLE proximity (deliberate action beats passive detection)

Hardware Requirements

Client side:

  • BLE 4.0+ receiver (USB dongle or built-in)
  • Optional: NFC reader (USB)
  • Commodity hardware: USB BLE dongles cost ~$5-10

Phone side:

  • Companion app (Android + iOS)
  • BLE 4.0+ (every phone since ~2013)
  • Background execution permission for BLE advertising

Implementation as Auth Plugin

struct BleProximityPlugin {
    scanner: BleScanner,
    known_tokens: HashMap<TokenId, ProximityState>,
    rssi_threshold: i8,     // e.g., -70 dBm
    attach_delay: Duration, // e.g., 2 seconds
    detach_delay: Duration, // e.g., 10 seconds
}

impl TokenProvider for BleProximityPlugin {
    fn watch(&self) -> TokenEventStream {
        // Emits:
        // TokenInserted(token_id) -- phone entered proximity
        // TokenRemoved(token_id)  -- phone left proximity
    }
}

This implements the same TokenProvider trait as smart cards (ADR-004). The session management layer doesn't know or care whether the token came from a smart card slot or a BLE beacon.

Companion App Scope

The phone app is intentionally minimal:

  1. One-time setup: authenticate with IdP (OIDC), receive signing key
  2. Background service: broadcast BLE beacon with rotating signed token
  3. Optional: respond to GATT challenges for mutual auth
  4. Optional: show notification when session attaches/detaches
  5. No remote desktop functionality -- the phone is a key, not a viewer

Platform:

  • Android: foreground service with BLE advertising
  • iOS: Core Bluetooth peripheral mode (works in background with limitations)
  • Could be a PWA using Web Bluetooth (limited background support)

Rationale

  • Zero-friction session mobility: walk up, session appears. Walk away, session suspends. No card to insert, no button to press, no QR to scan.
  • Users already carry phones: unlike smart cards which are an additional device to manage and can be forgotten
  • Maps to SunRay semantics: insert/remove maps to enter/leave proximity. Same session management, different physical mechanism.
  • Pluggable: implements TokenProvider trait. Composable with other token types. Smart card overrides BLE on explicit insertion.
  • Tunable security/convenience tradeoff: high security deployments add NFC tap requirement or disable BLE entirely. Casual deployments use pure proximity.

Consequences

  • Requires BLE hardware on client devices (USB dongle if not built-in)
  • Must develop and maintain companion apps for Android and iOS
  • BLE scanning has power implications on battery-powered clients
  • RSSI is noisy and affected by environment (walls, bodies, interference). Threshold tuning is deployment-specific.
  • iOS background BLE advertising has limitations (Apple throttles frequency)
  • Must handle edge cases: phone in adjacent room, phone dies mid-session, multiple phones
  • Privacy consideration: BLE beacons are detectable by nearby devices. Token payload must be encrypted so only WayRay clients can read the session token.