Skip to content

feat(socket-auth): authenticate realtime socket with driver socket tokens - #112

Merged
roncodes merged 13 commits into
feature/v3-redesignfrom
feature/socket-auth
Oct 14, 2026
Merged

roncodes merged 13 commits into
feature/v3-redesignfrom
feature/socket-auth

Conversation

@roncodes

@roncodes roncodes commented Oct 6, 2026 •

Copy link
Copy Markdown
Member

What

Authenticates the driver app's realtime (SocketCluster) connection with a short-lived socket token, as part of authenticated realtime channels.

  • Token provider (src/services/socket-auth.ts): POST {host}/{namespace}/socket/token with the driver's bearer token (same host/namespace the SDK adapter uses, default v1). Response {token, expires_in, expires_at}.
  • In-memory auth engine passed to socketClusterClient.create: loadToken fetches a fresh token when there is none or it is within 60 s of expiry; saveToken / removeToken keep it in memory only. The token never touches MMKV/localStorage and never goes in the socket URL.
  • Refresh at expires_in - 60 s via socket.authenticate(newToken) (expiry measured against the device clock from expires_in, so clock skew does not matter).
  • Recovery: on deauthenticate or a channel kickOut, fetch a new token, re-authenticate and resubscribe every channel the app still listens to. subscribeFail on a tracked channel retries the same way with backoff (1 s, 2 s, 4 s), then gives up. Channels are tracked from the provider's subscribeChannel / closeChannel / killChannel.
  • Session changes: the socket is recreated on login, logout (old socket disconnected, token dropped), organization switch, and instance-link reconfiguration. AuthContext exposes a sessionEpoch counter so an org switch re-authenticates even if the bearer token string does not change.
  • Client tag: handshake query: { client: 'navigator/<app version>' } (version from DeviceInfo on native, package.json on web).
  • Native and web contexts both updated.

Small fixes this needed:

  • Native subscribeChannel used to wait forever when a subscription was rejected. It now settles on subscribe or subscribeFail. On a rejection it still returns the channel, so the retry delivers data to the same listener.
  • OrderScreen now clears its listener ref on cleanup so it re-subscribes when the socket is recreated.

Why

Realtime channels are moving to authenticated subscriptions. With this change the driver app can only subscribe to channels the server authorizes for the signed-in driver.

Behaviour on older servers

The instance-link deep link can point this app at any self-hosted instance, including ones without socket auth. A 404 from the mint route (or no signed-in driver) means the socket connects anonymously, as it does today. The 404 is remembered for that socket session, so the app does not retry on every reconnect. Other mint failures (network, 5xx) also fall back to anonymous, with a 30 s retry backoff.

Test plan

  • New jest tests in __tests__/SocketAuth-test.js cover the token request, the 404 and no-driver anonymous fallback, the 60 s expiry window, the refresh timer, deauthenticate/kickOut recovery and resubscribe, subscribeFail backoff, and teardown.
  • Verified by CI / device QA.

Related PRs

Part of the authenticated realtime channels rollout (socket auth), one PR per repo:

mcnutt0316 and others added 12 commits July 27, 2026 10:50
resizePhoto measured Dimensions.get('window') — the device screen — rather
than the image at the given uri. Captured photos were scaled to the phone's
point size and into the screen's aspect ratio, so proof-of-delivery images
uploaded at a fraction of their real resolution.

Read the actual dimensions with Image.getSize, wrapped as a promise. On
failure it resolves null and resizePhoto returns the original uri, so an
unreadable image uploads full size rather than being lost.

Fixes #89
Fix POD photo resizing to use image dimensions instead of screen size
Both prefixes are honoured so a release branch opened before the rename still
releases. A merge that produces no tag and no publish looks exactly like a
successful one, so the cutover is deliberately not a flag day.
ci(release): adopt the release/v* branch convention
…kens

Mint a short-lived socket token for the signed-in driver from
POST {host}/{namespace}/socket/token and deliver it in the SocketCluster
handshake through an in-memory auth engine. The token is refreshed a
minute before expiry, and re-fetched when the server deauthenticates the
socket, kicks it out of a channel or rejects a subscription; tracked
channels are then resubscribed. Servers without the route (404) and
signed-out sessions connect anonymously as before.

The socket is recreated on login, logout, organization switch and
instance-link changes, and identifies itself with a navigator/<version>
client tag in the handshake query.
Conflicts came from the v2.0.11 release commits this branch carries:
- src/utils/index.js: keep the image-dimension resize fix, in v3's JSDoc style
- translations/en.json: keep v3's strings and main's location disclosure copy;
  drop the unused skipForNow
- package.json: drop main's jest key, which collides with v3's jest.config.js;
  its modulePathIgnorePatterns move there
@roncodes
roncodes merged commit 864fbbc into feature/v3-redesign Oct 14, 2026
@roncodes
roncodes deleted the feature/socket-auth branch October 14, 2026 09:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants