added realtime jwt support#1612
Conversation
Greptile SummaryThis PR adds JWT support to Realtime WebSocket connections across generated SDK templates. The main changes are:
Confidence Score: 5/5The realtime JWT support changes appear safe to merge. No blocking correctness or security issues were identified in the changed SDK templates.
What T-Rex did
Reviews (2): Last reviewed commit: "removed comments" | Re-trigger Greptile |
| {% if language.name == 'ReactNative' %} | ||
| const WebSocketCtor: any = WebSocket; | ||
| // On web the JWT must ride in the query string (no handshake headers); | ||
| // on native it goes in the `x-{{ spec.title | caseLower }}-jwt` header. | ||
| const socket = (this.socket = Platform.OS === 'web' | ||
| ? new WebSocketCtor(url) | ||
| ? new WebSocketCtor(jwt ? `${url}&jwt=${encodeURIComponent(jwt)}` : url) | ||
| : new WebSocketCtor(url, undefined, { | ||
| headers: { | ||
| Origin: `{{ spec.title | caseLower }}-${Platform.OS}://${this.client.config.platform}` | ||
| Origin: `{{ spec.title | caseLower }}-${Platform.OS}://${this.client.config.platform}`, | ||
| ...(jwt ? { 'x-{{ spec.title | caseLower }}-jwt': jwt } : {}) | ||
| } | ||
| })); |
There was a problem hiding this comment.
Stale JWT after rotation in new Realtime class
The new Realtime class (this file) reads this.client.config.jwt once inside createSocketLocked() and embeds it in the URL (web) or header (native) only at socket-creation time. Unlike the legacy client.ts.twig path — which caches this.realtime.url and compares it on every createSocket() call so that a JWT change causes a reconnect on the next subscribe() — this class has no URL-change detection. If the caller invokes client.setJwt(newToken) while an active subscription exists, the WebSocket continues using the old JWT until the connection naturally drops and reconnects. On native platforms the new JWT won't appear even after reconnect unless createSocketLocked() is re-entered, which requires all subscriptions to be torn down and re-created.
Consider either: (a) storing the JWT used at connection time and forcing a reconnect in createSocket() when it changes, or (b) documenting that callers must disconnect() and resubscribe after a JWT rotation.
| const jwt = this.client.config.jwt; | ||
| {% if language.name != 'ReactNative' %} | ||
| // Browsers cannot set headers on the WebSocket handshake, so the JWT | ||
| // travels in the `jwt` query string instead (the server accepts both). | ||
| if (jwt) { | ||
| queryParams += `&jwt=${encodeURIComponent(jwt)}`; | ||
| } |
There was a problem hiding this comment.
JWT exposed in WebSocket URL and server access logs
For all non-ReactNative (browser) targets the JWT is appended to the WebSocket upgrade URL as ?jwt=<token>. Because the HTTP Upgrade request is a standard HTTP GET, the full URL — including the JWT — will appear in server-side access logs, reverse-proxy logs, and load-balancer logs. Anyone with read access to those logs can extract a valid bearer token.
The same limitation applies to the browser and flutter-browser templates. If log-based token exposure is a concern, consider rotating short-lived JWTs and ensuring log-scrubbing is in place on the server side.
What does this PR do?
(Provide a description of what this PR does.)
Test Plan
(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work.)
Related PRs and Issues
(If this PR is related to any other PR or resolves any issue or related to any issue link all related PR and issues here.)
Have you read the Contributing Guidelines on issues?
(Write your answer here.)