diff --git a/templates/android/library/src/main/java/io/package/services/Realtime.kt.twig b/templates/android/library/src/main/java/io/package/services/Realtime.kt.twig index c2301d400c..269c3d0403 100644 --- a/templates/android/library/src/main/java/io/package/services/Realtime.kt.twig +++ b/templates/android/library/src/main/java/io/package/services/Realtime.kt.twig @@ -73,7 +73,12 @@ class Realtime(client: Client) : Service(client), CoroutineScope { val encodedProject = java.net.URLEncoder.encode(client.config["project"].toString(), "UTF-8") val queryParams = "project=$encodedProject" val url = "${client.endpointRealtime}/realtime?$queryParams" - val request = Request.Builder().url(url).build() + val requestBuilder = Request.Builder().url(url) + val jwt = client.config["jwt"] + if (!jwt.isNullOrEmpty()) { + requestBuilder.addHeader("x-{{ spec.title | caseLower }}-jwt", jwt) + } + val request = requestBuilder.build() val generation = socketGeneration.incrementAndGet() socket = client.http.newWebSocket(request, {{ spec.title | caseUcfirst }}WebSocketListener(generation)) diff --git a/templates/apple/Sources/Services/Realtime.swift.twig b/templates/apple/Sources/Services/Realtime.swift.twig index 255cd0dea3..c231bd9bbb 100644 --- a/templates/apple/Sources/Services/Realtime.swift.twig +++ b/templates/apple/Sources/Services/Realtime.swift.twig @@ -135,9 +135,15 @@ open class Realtime : Service { return } + var headers = HTTPHeaders() + if let jwt = client.config["jwt"], !jwt.isEmpty { + headers.add(name: "x-{{ spec.title | caseLower }}-jwt", value: jwt) + } + socketClient = WebSocketClient( url, tlsEnabled: !client.selfSigned, + headers: headers, delegate: self ) diff --git a/templates/flutter/lib/src/realtime_browser.dart.twig b/templates/flutter/lib/src/realtime_browser.dart.twig index 3563e7d983..a0f4193ab4 100644 --- a/templates/flutter/lib/src/realtime_browser.dart.twig +++ b/templates/flutter/lib/src/realtime_browser.dart.twig @@ -22,6 +22,15 @@ class RealtimeBrowser extends RealtimeBase with RealtimeMixin { Future _getWebSocket(Uri uri) async { await (client as ClientBrowser).init(); + + final jwt = client.config['jwt']; + if (jwt != null && jwt.isNotEmpty) { + uri = uri.replace(queryParameters: { + ...uri.queryParameters, + 'jwt': jwt, + }); + } + return HtmlWebSocketChannel.connect(uri); } diff --git a/templates/flutter/lib/src/realtime_io.dart.twig b/templates/flutter/lib/src/realtime_io.dart.twig index e8cd2ebb3d..d45450dffc 100644 --- a/templates/flutter/lib/src/realtime_io.dart.twig +++ b/templates/flutter/lib/src/realtime_io.dart.twig @@ -32,6 +32,11 @@ class RealtimeIO extends RealtimeBase with RealtimeMixin { final cookies = await (client as ClientIO).cookieJar.loadForRequest(uri); headers = {HttpHeaders.cookieHeader: CookieManager.getCookies(cookies)}; + final jwt = client.config['jwt']; + if (jwt != null && jwt.isNotEmpty) { + headers['x-{{ spec.title | caseLower }}-jwt'] = jwt; + } + final websok = IOWebSocketChannel((client as ClientIO).selfSigned ? await _connectForSelfSignedCert(uri, headers) : await WebSocket.connect(uri.toString(), headers: headers)); diff --git a/templates/react-native/src/client.ts.twig b/templates/react-native/src/client.ts.twig index 5d1c60cb5b..8ba12235ed 100644 --- a/templates/react-native/src/client.ts.twig +++ b/templates/react-native/src/client.ts.twig @@ -328,6 +328,11 @@ class Client { const channels = new URLSearchParams(); channels.set('project', this.config.project); + const jwt = this.config.jwt; + if (jwt && Platform.OS === 'web') { + channels.set('jwt', jwt); + } + const url = this.config.endpointRealtime + '/realtime?' + channels.toString(); if ( @@ -347,7 +352,8 @@ class Client { // @ts-ignore this.realtime.socket = new WebSocket(url, undefined, { headers: { - Origin: `{{ spec.title | caseLower }}-${Platform.OS}://${this.config.platform}` + Origin: `{{ spec.title | caseLower }}-${Platform.OS}://${this.config.platform}`, + ...(jwt && Platform.OS !== 'web' ? { 'x-{{ spec.title | caseLower }}-jwt': jwt } : {}) } }); this.realtime.socket.addEventListener('message', this.realtime.onMessage); diff --git a/templates/unity/Assets/Runtime/Realtime.cs.twig b/templates/unity/Assets/Runtime/Realtime.cs.twig index 64f8dcc1b5..b644d55a0a 100644 --- a/templates/unity/Assets/Runtime/Realtime.cs.twig +++ b/templates/unity/Assets/Runtime/Realtime.cs.twig @@ -523,17 +523,24 @@ namespace {{ spec.title | caseUcfirst }} { var uri = PrepareUri(); Debug.Log($"[Realtime] Connecting to URI: {uri}"); - + + var headers = new Dictionary(); + var jwt = _client.Config.GetValueOrDefault("jwt"); + if (!string.IsNullOrEmpty(jwt)) + { + headers["x-{{ spec.title | caseLower }}-jwt"] = jwt; + } + if (_webSocket == null || _webSocket.State == WebSocketState.Closed) { - _webSocket = new WebSocket(uri); + _webSocket = new WebSocket(uri, headers); _lastUrl = uri; SetupWebSocketEvents(); } else if (_lastUrl != uri && _webSocket.State != WebSocketState.Closed) { await CloseConnection(); - _webSocket = new WebSocket(uri); + _webSocket = new WebSocket(uri, headers); _lastUrl = uri; SetupWebSocketEvents(); } diff --git a/templates/web/src/client.ts.twig b/templates/web/src/client.ts.twig index 40eb362d59..9744bdf3f9 100644 --- a/templates/web/src/client.ts.twig +++ b/templates/web/src/client.ts.twig @@ -564,7 +564,11 @@ class Client { const encodedProject = encodeURIComponent((this.config.project as string) ?? ''); // URL carries only the project; channels/queries are sent via subscribe message. - const queryParams = 'project=' + encodedProject; + let queryParams = 'project=' + encodedProject; + + if (this.config.jwt) { + queryParams += '&jwt=' + encodeURIComponent(this.config.jwt as string); + } const url = this.config.endpointRealtime + '/realtime?' + queryParams; diff --git a/templates/web/src/services/realtime.ts.twig b/templates/web/src/services/realtime.ts.twig index 2d4514718c..5dc18d8878 100644 --- a/templates/web/src/services/realtime.ts.twig +++ b/templates/web/src/services/realtime.ts.twig @@ -208,7 +208,14 @@ export class Realtime { } // URL carries only the project; channels/queries are sent via the subscribe message. - const queryParams = `project=${projectId}`; + let queryParams = `project=${projectId}`; + + const jwt = this.client.config.jwt; +{% if language.name != 'ReactNative' %} + if (jwt) { + queryParams += `&jwt=${encodeURIComponent(jwt)}`; + } +{% endif %} const endpoint = this.client.config.endpointRealtime !== '' @@ -243,10 +250,11 @@ export class Realtime { {% if language.name == 'ReactNative' %} const WebSocketCtor: any = WebSocket; 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 } : {}) } })); {% else %}