Looks like it's easy to add custom HTTP headers to your websocket client with any HTTP header client which supports this, but I can't find how to do it with the web platform's WebSocket API.
Anyone has a clue on how to achieve it?
var ws = new WebSocket("ws://example.com/service");
Specifically, I need to be able to send an HTTP Authorization header.
Updated 2x
Short answer: No, only the path and protocol field can be specified.
Longer answer:
There is no method in the JavaScript WebSockets API for specifying additional headers for the client/browser to send. The HTTP path ("GET /xyz") and protocol header ("Sec-WebSocket-Protocol") can be specified in the WebSocket constructor.
The Sec-WebSocket-Protocol header (which is sometimes extended to be used in websocket specific authentication) is generated from the optional second argument to the WebSocket constructor:
var ws = new WebSocket("ws://example.com/path", "protocol");
var ws = new WebSocket("ws://example.com/path", ["protocol1", "protocol2"]);
The above results in the following headers:
Sec-WebSocket-Protocol: protocol
and
Sec-WebSocket-Protocol: protocol1, protocol2
A common pattern for achieving WebSocket authentication/authorization is to implement a ticketing system where the page hosting the WebSocket client requests a ticket from the server and then passes this ticket during WebSocket connection setup either in the URL/query string, in the protocol field, or required as the first message after the connection is established. The server then only allows the connection to continue if the ticket is valid (exists, has not been already used, client IP encoded in ticket matches, timestamp in ticket is recent, etc). Here is a summary of WebSocket security information: https://devcenter.heroku.com/articles/websocket-security
Basic authentication was formerly an option but this has been deprecated and modern browsers don't send the header even if it is specified.
Basic Auth Info (Deprecated - No longer functional):
NOTE: the following information is no longer accurate in any modern browsers.
The Authorization header is generated from the username and password (or just username) field of the WebSocket URI:
var ws = new WebSocket("ws://username:password#example.com")
The above results in the following header with the string "username:password" base64 encoded:
Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=
I have tested basic auth in Chrome 55 and Firefox 50 and verified that the basic auth info is indeed negotiated with the server (this may not work in Safari).
Thanks to Dmitry Frank's for the basic auth answer
More of an alternate solution, but all modern browsers send the domain cookies along with the connection, so using:
var authToken = 'R3YKZFKBVi';
document.cookie = 'X-Authorization=' + authToken + '; path=/';
var ws = new WebSocket(
'wss://localhost:9000/wss/'
);
End up with the request connection headers:
Cookie: X-Authorization=R3YKZFKBVi
Sending Authorization header is not possible.
Attaching a token query parameter is an option. However, in some circumstances, it may be undesirable to send your main login token in plain text as a query parameter because it is more opaque than using a header and will end up being logged whoknowswhere. If this raises security concerns for you, an alternative is to use a secondary JWT token just for the web socket stuff.
Create a REST endpoint for generating this JWT, which can of course only be accessed by users authenticated with your primary login token (transmitted via header). The web socket JWT can be configured differently than your login token, e.g. with a shorter timeout, so it's safer to send around as query param of your upgrade request.
Create a separate JwtAuthHandler for the same route you register the SockJS eventbusHandler on. Make sure your auth handler is registered first, so you can check the web socket token against your database (the JWT should be somehow linked to your user in the backend).
HTTP Authorization header problem can be addressed with the following:
var ws = new WebSocket("ws://username:password#example.com/service");
Then, a proper Basic Authorization HTTP header will be set with the provided username and password. If you need Basic Authorization, then you're all set.
I want to use Bearer however, and I resorted to the following trick: I connect to the server as follows:
var ws = new WebSocket("ws://my_token#example.com/service");
And when my code at the server side receives Basic Authorization header with non-empty username and empty password, then it interprets the username as a token.
You cannot add headers but, if you just need to pass values to the server at the moment of the connection, you can specify a query string part on the url:
var ws = new WebSocket("ws://example.com/service?key1=value1&key2=value2");
That URL is valid but - of course - you'll need to modify your server code to parse it.
You can not send custom header when you want to establish WebSockets connection using JavaScript WebSockets API.
You can use Subprotocols headers by using the second WebSocket class constructor:
var ws = new WebSocket("ws://example.com/service", "soap");
and then you can get the Subprotocols headers using Sec-WebSocket-Protocol key on the server.
There is also a limitation, your Subprotocols headers values can not contain a comma (,) !
For those still struggling in 2021, Node JS global web sockets class has an additional options field in the constructor. if you go to the implementation of the the WebSockets class, you will find this variable declaration. You can see it accepts three params url, which is required, protocols(optional), which is either a string, an array of strings or null. Then a third param which is options. our interest, an object and (still optional). see ...
declare var WebSocket: {
prototype: WebSocket;
new (
uri: string,
protocols?: string | string[] | null,
options?: {
headers: { [headerName: string]: string };
[optionName: string]: any;
} | null,
): WebSocket;
readonly CLOSED: number;
readonly CLOSING: number;
readonly CONNECTING: number;
readonly OPEN: number;
};
If you are using a Node Js library like react , react-native. here is an example of how you can do it.
const ws = new WebSocket(WEB_SOCKETS_URL, null, {
headers: {
['Set-Cookie']: cookie,
},
});
Notice for the protocols I have passed null. If you are using jwt, you can pass the Authorisation header with Bearer + token
Disclaimer, this might not be supported by all browsers outside the box, from the MDN web docs you can see only two params are documented.
see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#syntax
Totally hacked it like this, thanks to kanaka's answer.
Client:
var ws = new WebSocket(
'ws://localhost:8080/connect/' + this.state.room.id,
store('token') || cookie('token')
);
Server (using Koa2 in this example, but should be similar wherever):
var url = ctx.websocket.upgradeReq.url; // can use to get url/query params
var authToken = ctx.websocket.upgradeReq.headers['sec-websocket-protocol'];
// Can then decode the auth token and do any session/user stuff...
In my situation (Azure Time Series Insights wss://)
Using the ReconnectingWebsocket wrapper and was able to achieve adding headers with a simple solution:
socket.onopen = function(e) {
socket.send(payload);
};
Where payload in this case is:
{
"headers": {
"Authorization": "Bearer TOKEN",
"x-ms-client-request-id": "CLIENT_ID"
},
"content": {
"searchSpan": {
"from": "UTCDATETIME",
"to": "UTCDATETIME"
},
"top": {
"sort": [
{
"input": {"builtInProperty": "$ts"},
"order": "Asc"
}],
"count": 1000
}}}
to all future debugger - until today i.e 15-07-21
Browser also don't support sending customer headers to the server, so any such code
import * as sock from 'websocket'
const headers = {
Authorization: "bearer " + token
};
console.log(headers);
const wsclient = new sock.w3cwebsocket(
'wss://' + 'myserver.com' + '/api/ws',
'',
'',
headers,
null
);
This is not going to work in browser. The reason behind that is browser native Websocket constructor does not accept headers.
You can easily get misguided because w3cwebsocket contractor accepts headers as i have shown above. This works in node.js however.
The recommended way to do this is through URL query parameters
// authorization: Basic abc123
// content-type: application/json
let ws = new WebSocket(
"ws://example.com/service?authorization=basic%20abc123&content-type=application%2Fjson"
);
This is considered a safe best-practice because:
Headers aren't supported by WebSockets
Headers are advised against during the HTTP -> WebSocket upgrade because CORS is not enforced
SSL encrypts query paramaters
Browsers don't cache WebSocket connections the same way they do with URLs
What I have found works best is to send your jwt to the server just like a regular message. Have the server listening for this message and verify at that point. If valid add it to your stored list of connections. Otherwise send back a message saying it was invalid and close the connection. Here is the client side code. For context the backend is a nestjs server using Websockets.
socket.send(
JSON.stringify({
event: 'auth',
data: jwt
})
);
My case:
I want to connect to a production WS server a www.mycompany.com/api/ws...
using real credentials (a session cookie)...
from a local page (localhost:8000).
Setting document.cookie = "sessionid=foobar;path=/" won't help as domains don't match.
The solution:
Add 127.0.0.1 wsdev.company.com to /etc/hosts.
This way your browser will use cookies from mycompany.com when connecting to www.mycompany.com/api/ws as you are connecting from a valid subdomain wsdev.company.com.
You can pass the headers as a key-value in the third parameter (options) inside an object.
Example with Authorization token. Left the protocol (second parameter) as null
ws = new WebSocket(‘ws://localhost’, null, { headers: { Authorization: token }})
Edit: Seems that this approach only works with nodejs library not with standard browser implementation. Leaving it because it might be useful to some people.
Technically, you will be sending these headers through the connect function before the protocol upgrade phase. This worked for me in a nodejs project:
var WebSocketClient = require('websocket').client;
var ws = new WebSocketClient();
ws.connect(url, '', headers);
Related
I'm developing a fullstack app with Node + Express backend and NextJS front end (separate servers) and am having trouble requesting the browser to attach the cookie vended down as part of the response header from the node server. Here's the setup:
Node server is running on localhost:3000 and NextJs server is running on localhost:3001.
I have set up alias in etc/hosts to route someAlias.com to 127.0.0.1.
Using the front end UI (port 3001) I was able to vend the cookie with JsHttp's cookie module with the following code from the backend (port 3000):
import { serialize } from 'cookie';
...
const cookie = serialize(TOKEN_NAME, TOKEN_VAL, {
httpOnly: true,
sameSite: 'none',
});
I was able to observe the Set-Cookie header in the response.
However, in the subsequent requests, I did not see the cookie being attached. I have tried fiddling with the above cookie serialization params with no success:
Here are the arguments I've tried:
domain: ['.someAlias.com:3000', '.someAlias.com:3001']
path: '/'
domain: '.someAlias.com'
I have a feeling it might just be due to front end and back end server ports being different, but all requests have been initiated on the client side going to localhost:3000 (backend port). So not sure what I've possibly done wrong here.
====== UPDATE =======
I've run a couple more experiments, and found out that when I'm accessing a URL directly, NextJs renders the page server-side. When I'm transitioning between pages within the app, the page is rendered client-side where it queries the backend port 3000 directly. Unfortunately in neither scenario did I see any cookie being set...
the cookies must be sent directly from the browser to the server , which is not the case when you use nextJs . because when you access to your app next js will server side render your page and then the request will be sent from nextjs server to your nodejs server so the browser will send the cookies to nextjs server not to your nodejs server .
the solution is to send cookies manually from nextjs server to nodejs .
example with fetchApi and getServerSideProps function:
export async function getServerSideProps(context){
try{
const res = await fetch(`your-api-endpoint`, {
method: 'GET',
credentials:'include',
headers: {
'Access-Control-Allow-Credentials': true,
Cookie: context.req.headers.cookie
},
})
const data = await res.json()
if(!res.ok){
throw data
}
}catch(err){
return console.log(err)
}
return {
props: {
data
}
}
}
SAMESITE NONE
If you use SameSite=none you also need to use Secure, meaning SSL must also be used. You are then saying that your 2 servers are from unrelated domains, which is probably not what you want, since browsers will drop cookies aggressively.
LOCAL PC OPTIONS
Use these settings initially on your development computer, and ensure that all URLs used in the browser and Ajax calls use http://somealias.com:3000 and http://somealias.com:3001 rather than localhost. Cookies will then stick on a development computer.
Domain=.somealias.com
Path=/
SameSite=strict
HTTP Only
DEPLOYED OPTIONS
When you deploy to a proper environment, also use SSL and set the cookie option Secure. Most importantly, the two domains must meet hosting prerequisites of sharing the same base domain, eg:
https://www.example.com
https://api.example.com
This ensures that cookies issued are considered first party and in the same site, so that they are not dropped. If preconditions are not met, there is nothing you can do in code to fix the problem.
SIMILAR RESOURCE
This Curity code example uses local development domains and same site cookie settings similar to those I've used above and may be useful to compare against.
You should set serialized cookie with res.set Express method.
Alternatively, you can use res.cookie method without additional cookie package like this:
res.cookie(TOKEN_NAME, TOKEN_VAL, {
httpOnly: true,
sameSite: 'none',
});
Note, you shouldn't worry about different ports on the same domain, since cookies are not isolated by port but domain only. No matter what port you use, cookies should be visible.
Since you said, "I was able to observe the Set-Cookie header in the response", I believe your node.js setting correct.
When you get response from node js, you need to set cookies, which can be done with a npm packages easily. I will demonstrate with js-cookie:
import Cookies from "js-cookie";
you write a reusable function to set the cookies:
// what ever your project returns
setSession(authResult) {
//converting everything to miliseconds
const expiresAt =
JSON.stringify(authResult.expiresIn * 1000) + new Date().getTime();
// I just put properties. I dont know how project sets
Cookies.set("user", authResult.idTokenPayload);
Cookies.set("jwt", authResult.idToken);
Cookies.set("expiresAt", expiresAt);
}
Everytime you make request you have to set headers. You have to retrieve cookies based on if you are on browser or on server. So you have to write a function if you are on server. Since I demonstrated how to set cookies with js-cookies, you can get the cookies easily on the browser. This reusable function to retrieve the cookie if you are on the server:
// cookieKey: I set three "user", "jwt","expiresAt"
export const getCookieFromReq = (req, cookieKey) => {
console.log("req.headers", req.headers);
// cookies are attached to the req.header.cookie.
const cookie = req.headers.cookie
.split(";")
.find((c) => c.trim().startsWith(`${cookieKey}=`));
if (!cookie) return undefined;
return cookie.split("=")[1];
};
Now you have to write a function to set the headers:
import Cookies from "js-cookie";
import { getCookieFromReq } from "./directoryOf";
export const setAuthHeader = (req) => {
const token = req ? getCookieFromReq(req, "jwt") : Cookies.getJSON("jwt");
if (token) {
return {
headers: { authorization: `Bearer ${token}` },
};
}
return undefined;
};
Now when you make request, you have to use this setAuthHeader. For example:
await axiosInstance
.post("/blogs", blogData, setAuthHeader())
.then((response) => response.data)
.catch((error) => rejectPromise(error));
I've implemented a server using Spring Stomp and now I'm trying to connect to my server using stomp-js rx-stomp.
What I find really awkward is that the JS implementation is not working, although I've managed to make it work using the Java stomp client.
Java client code(works):
WebSocketStompClient stompClient = new WebSocketStompClient(new SockJsClient(createTransportClient()));
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
final String URL = "http://localhost:" + port + "/ws";
// -< Headers used for authentication
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
String user = "user1", pass = "abcd1234";
headers.add("Authorization", "Basic " + getBasicAuthToken(user, pass));
StompSession stompSession = stompClient.connect(URL, headers, new StompSessionHandlerAdapter() {
}).get(10, TimeUnit.SECONDS);
JS client code(doesn't work):
connect: function() {
const stompConfig = {
connectHeaders: {
login: "user1",
passcode: "abcd1234",
Authorization: "Basic dXNlcjE6YWJjZDEyMzQ="
},
webSocketFactory: function() {
return new SockJS("http://localhost:8080/ws");
},
reconnectDelay: 60000
};
rxStomp = new RxStomp.RxStomp();
rxStomp.configure(stompConfig);
rxStomp.activate();
rxStomp.connected$.subscribe(res => {
if (res === 1) console.log('connected');
else console.log('not connected');
});
}
First of all, I find really awkward that I see a prompt asking my to enter a username and a password. If I enter the credentials there then the client connects successfully. So, I thought that I must be doing something wrong regarding the connect headers. As you can see, I've tried to add the Basic Auth token there, hoping that it would solve something. It doesn't.
The Java and the Javascript versions of the code, even though similar, differ in an important way. The Java version sets the Authorization header in the underlying HTTP connection of the Websocket. However, in the Javascript version, the HTTP connection is made, and then the Authorization header is passed as the STOMP CONNECT frame.
The browser Websocket API or SockJS does not allow setting custom headers to the underlying HTTP connection, which is used by the Java version of the code in the question. To support authentication, the brokers need to support receiving authentication parameters as part of the CONNECT frame (exposed as connectHeaders in the JS Stomp clients).
Spring does not, by default, support authentication parameters as part of the CONNECT frame. Please see https://docs.spring.io/spring-framework/docs/current/reference/html/web.html#websocket-stomp-authentication-token-based to support it.
I'm trying to query a VoltDB using its api:
const url = 'http://server:8080/api/1.0/'
const queryParam = encodeURIComponent('select * from table')
const queryURL = url + `?Procedure=#AdHoc&Parameters=['${queryParam}']&jsonp=console.log`
fetch(queryURL).then( response => {
response.text().then( text => console.log(text) )
})
With that code throws an "No Access-Control-Allow-Origin" error.
If I change the fetch call to this:
fetch(queryURL, { mode: 'no-cors').then( response => {
response.text().then( text => console.log(text) )
})
It does nothing
This is a browser security feature. If you are serving a web page from one url and within the page you have embedded url calls to another host or port, then the browser won't allow this.
One way to get around this is to add a proxy to your web server, so it can make the calls to port 8080, and pass the responses back to the web page from the same origin.
You may see some answers on Stack Overflow about using CORS to get around this error, but that requires changing the headers that VoltDB uses on port 8080, so that's not something you can do yourself, and we have no plans to do that.
Another solution is to use the voltdb.js file provided in some of our demos, such as the NBBO demo dashboard: https://github.com/VoltDB/voltdb/tree/master/examples/nbbo/web
I think this uses low-level javascript to open a socket to make the HTTP call without using XMLHttpRequest, so it avoids the No Access-Control-Allow-Origin error.
In the example, the code that is specific to the NBBO example is in demo.js, voltdb-dashboard.js contains code that is common to various example dashboards, and voltdb.js is the base library that provides access to call procedures asynchronously.
You should encode all the URI parameters not just the procedure parameters
$ curl --data 'Procedure=#AdHoc&Parameters=["select count(*) from store;"]' http://127.0.0.1:8080/api/1.0/
{"status":1,"appstatus":-128,"statusstring":null,"appstatusstring":null,"results":[{"status":-128,"schema":[{"name":"C1","type":6}],"data":[[100000]]}]}
or
$ curl --data 'Procedure=%40AdHoc&Parameters=%5B%22select+count(*)+from+store%3B%22%5D' http://127.0.0.1:8080/api/1.0/; echo
{"status":1,"appstatus":-128,"statusstring":null,"appstatusstring":null,"results":[{"status":-128,"schema":[{"name":"C1","type":6}],"data":[[100000]]}]}
Setup is an Ember frontend with a rails backend using JSON api.
Everything is going fine but some questions do come up:
How do I ensure only the emberjs application consumes the api? I wouldn't want a scripter to write an application to consume the backend api.
It all seems pretty insecure because the EmberJS application would come in a .js file to the client.
How would I ensure a user is really that user if everyone has access to a JS console?
You can extend the RESTAdapter and override the ajax method to include your authentication token in the hash, and you need make sure your controllers validate that token.
In my environment (.NET), I have the authentication token in a hidden field of the document which my app renders, so my ajax override looks like this:
App.Adapter = DS.RESTAdapter.extend({
ajax: function(url, type, hash, dataType) {
hash.url = url;
hash.type = type;
hash.dataType = dataType || 'json';
hash.contentType = 'application/json; charset=utf-8';
hash.context = this;
if (hash.data && type !== 'GET') {
hash.data = JSON.stringify(hash.data);
}
var antiForgeryToken = $('#antiForgeryTokenHidden').val();
if (antiForgeryToken) {
hash = {
'RequestVerificationToken': antiForgeryToken
};
}
jQuery.ajax(hash);
}
});
The token can come from a cookie or whatever you define, as long as you're able to include it in the request header and have your controllers validate it (possibly in before_filter), it should enough.
Then in the Store, pass the new adapter instead of the default (which is RESTAdapter)
App.Store = DS.Store.extend({
revision: 12,
adapter: App.Adapter.create()
})
Note: RESTAdapter#ajax will be changed in favor or Ember.RSVP, making this override deprecated. It must be updated after the next release, but should be ok for revision 12.
I am using Ember Simple Auth to great effect for user authentication and API authorisation.
I use the Oauth 2 user password grant type for authentication of the user and authorising the application by way of a bearer token which must be sent on all future API requests. This means the user enters their username/email and password into the client app which then sends to the server via HTTPS to get an authorisation token and possibly a refresh token. All requests must be over HTTPS to protect disclosure of the bearer token.
I have this in app/initializers/auth:
Em.Application.initializer
name: 'authentication'
initialize: (container, application) ->
Em.SimpleAuth.Authenticators.OAuth2.reopen
serverTokenEndpoint: 'yourserver.com/api/tokens'
Em.SimpleAuth.setup container, application,
authorizerFactory: 'authorizer:oauth2-bearer'
crossOriginWhitelist: ['yourserver.com']
In app/controllers/login.coffee:
App.LoginController = Em.Controller.extend Em.SimpleAuth.LoginControllerMixin,
authenticatorFactory: 'ember-simple-auth-authenticator:oauth2-password-grant'
In app/routes/router.coffee:
App.Router.map ->
#route 'login'
# other routes as required...
In app/routes/application.coffee:
App.ApplicationRoute = App.Route.extend Em.SimpleAuth.ApplicationRouteMixin
In app/routes/protected.coffee:
App.ProtectedRoute = Ember.Route.extend Em.SimpleAuth.AuthenticatedRouteMixin
In templates/login.hbs (I am using Ember EasyForm):
{{#form-for controller}}
{{input identification
label="User"
placeholder="you#example.com"
hint='Enter your email address.'}}
{{input password
as="password"
hint="Enter your password."
value=password}}
<button type="submit" {{action 'authenticate' target=controller}}>Login</button>
{{/form-for}}
To protect a route I just extend from App.ProtectedRoute or use the protected route mixin.
Your server will need to handle the Oauth 2 request and response at the configured server token endpoint above. This is very easy to do, Section 4.3 of RFC 6749 describes the request and response if your server side framework doesn't have built-in support for Oauth2. You will need to store, track and expire these tokens on your server however. There are approaches to avoiding storage of tokens but that's beyond the scope of the question :)
I have answered the backend question and provided example rails example code for user authentication, API authorisation and token authentication here
Does anyone know if it is possible to send basic http authentication credentials with EventSource?
I'm looking for a solution to the same problem. This post here says this:
Another caveat is that as far as we know, you cannot change the HTTP
headers when using EventSource, which means you have to submit an
authorization query string param with the value that you would have
inserted using HTTP Basic Auth: a base64 encoded concatenation of your
login and a token.
Here is the code from the post:
// First, we create the event source object, using the right URL.
var url = "https://stream.superfeedr.com/?";
url += "&hub.mode=retrieve";
url += "&hub.topic=http%3A%2F%2Fpush-pub.appspot.com%2Ffeed";
url += "&authorization=anVsaWVuOjJkNTVjNDhjMDY5MmIzZWFkMjA4NDFiMGViZDVlYzM5";
var source = new EventSource(url);
// When the socket has been open, let's cleanup the UI.
source.onopen = function () {
var node = document.getElementById('sse-feed');
while (node.hasChildNodes()) {
node.removeChild(node.lastChild);
}
};
// Superfeedr will trigger 'notification' events, which corresponds
// exactly to the data sent to your subscription endpoint
// (webhook or XMPP JID), with a JSON payload by default.
source.addEventListener("notification", function(e) {
var notification = JSON.parse(e.data);
notification.items.sort(function(x, y) {
return x.published - y.published;
});
notification.items.forEach(function(i) {
var node = document.getElementById('sse-feed');
var item = document.createElement("li");
var t = document.createTextNode([new Date(i.published * 1000), i.title, i.content].join(' '));
item.appendChild(t);
node.insertBefore(item, node.firstChild);
// We add the element to the UI.
});
});
If your talk about cookies (not http auth):
EventSource uses http, so cookies are sent with the EventSource connection request.
Http auth should be supported as any other http url, although from the spec CORS+http auth is not supported.
Nowadays there is a NPM package to change the HTTP Header
https://www.npmjs.com/package/eventsource
This library is a pure JavaScript implementation of the EventSource
client. The API aims to be W3C compatible.
You can use it with Node.js or as a browser polyfill for browsers that
don't have native EventSource support.
You can use event-source-polyfill to add headers like this
import { EventSourcePolyfill } from 'event-source-polyfill';
new EventSourcePolyfill(`/api/liveUpdate`, {
headers: {
Authorization: `Bearer 12345`,
'x-csrf-token': `xxx-xxx-xxx`,
},
});
EventSource is about the server sending events to the client. I think you need bidirectional communication for authentication. How would you otherwise send the actual credentials?
WebSockets, however, can achieve that. Is that what you are looking for?
Update:
You can achieve what you want by utilizing cookies, as pointed out by 4esn0k. Cookies are sent along with the initial request that the browser makes to establish the connection. So, just make sure you set the session identifier for the cookie before launching any EventSource connections.