Configuring authentication headers for WebSocket connection - javascript

IIUC, when I create a WebSocket an HTTP request is sent to the specified URL, containing an upgrade request. Is it typicaly to pass authentication information along with this upgrade request, or should it be performed separately?
var websocket = new WebSocket("ws://domain:port/foo"); // Can I include authentication headers with the initial upgrade HTTP request?

The WebSocket RFC standard doesn't define any protocol-specific client authentication mechanism but mentions that HTTP authentication is a possible option:
10.5. WebSocket Client Authentication
This protocol doesn't prescribe any particular way that servers can
authenticate clients during the WebSocket handshake. The WebSocket
server can use any client authentication mechanism available to a
generic HTTP server, such as cookies, HTTP authentication, or TLS
authentication.
The standard for http URLs prescribes a form which includes the login credentials within the URL. The form is http://username:password#www.example.com/file. But this syntax is not supported by all browsers because, frankly, it was a really bad idea.
The WebSocket API does not expose any features intended for HTTP client authentication. That means web browsers are supposed to provide authentication the way they usually do: With the URL syntax above when they decide to support it or by showing a popup to the user where they enter their login credentials.

You should do the authentication through web, return a cookie and then connect to the websocket server again, carrying the cookie. The WS server can validate the cookie
If there is no cookie based authentication or it is just not possible (like the WS server in another domain), you will have to create your own request-response messages for login.

Related

Validate WebSocket data

I have a WebSocket server written in javascript and send data to it from my CSharp application. Now how can I make sure that these are correct? I thought I could do something with hash values but I don't know how to do that. Does anyone have an idea or code example?
The first thing to understand is the types of WebSocket protocol/transports. WebSocket ws:// transport is basically unusable in terms of security as it uses HTTP. The wss:// protocol establishes a secure connection over TCP/HTTPS. The wss protocol, therefore, protects against man-in-the-middle attacks.
There is multiple methods to authenticate a user when setting up a WebSocket connection, and none are perfect. Since the standard WebSocket usage prevents additional headers from being set such as custom authentication headers, tried and true methods that would be used in a standard HTTPS request to verify the validity of a client can't be used.
The link here outlines some common methods to keep the client and server in sync while setting up a WebSocket connection, and still add some security so the server can keep track of what clients are opening WebSocket connections. There are a lot of workarounds listed for the server to safely receive sensitive, authentication data from the client.

Should the OAuth2 Redirect URL be to the frontend or backend?

I'm setting up OAuth2 in my app using the Authorization Grant flow. I am also using create-react-app, such that I'm developing on localhost:3000, which proxies to my app server backend on localhost:8080.
Everything mostly works, except for the fact that I cannot get the CSRF token working.
I realized it was because I was having the OAuth2 Redirect URL set to the backend, and as a result it was not sending the private encrypted csrf_state cookie along, because the request was originating from google instead of my app.
I don't think this will be a problem in production, because there won't be a proxy server. Instead, both the backend and frontend will be served from the same mydomain.com
So, should I just not have this work in development? Or should I have the OAuth2 redirect URL set to my frontend (localhost:3000), which then automatically redirects to the backend (localhost:8080), such that it can send the private encrypted CSRF token along?
Or is there a way to have the cookie originate from google, without having the multiple redirects? Or should I just not bother with CSRF, since SameSite has such large support amongst browsers now?
The OAuth2.0 Authorization Code grant includes CSRF protection using the state parameter. Use this instead of relying on cookies.
state
RECOMMENDED. An opaque value used by the client to maintain
state between the request and callback. The authorization
server includes this value when redirecting the user-agent back
to the client. The parameter SHOULD be used for preventing
cross-site request forgery as described in Section 10.12.
Source: https://www.rfc-editor.org/rfc/rfc6749#section-4.1
Ahmad is right - and here is some more context on standard usage for react apps and APIs:
If you're using React then you have an SPA that should redirect directly to Google during logins
So your redirect url should be localhost:3000
Your SPA should be entirely cookieless - and much simpler - which is one of the benefits of SPAs - also you can turn off CSRF checks in the API
Your SPA will then send an access token to your API and the API will need to validate the token rather than cookies
My tutorial and code sample may help you understand the moving parts:
https://authguidance.com/2017/09/24/basicspa-overview/

How to set cookie header for webSocket javascript?

I am trying to open a websocket to a server with kerberos authentication, error during handshake occurs (error code : 400) ;
i saw it's not possible to send credentials through web socket and what i have to do is to set the username and password through web socket cookie and the server will read them.
So how can i set cookies for web socket ?
thank you,
It depends on the browser. You may implement handling cookies if they arrive with the initial HTTP request to initiate a WebSocket connection, but if you can't require your users to, say, use Safari, which sends cookies with WebSocket open requests, and not Chrome, which does not, you'll probably have to implement a mechanism for the client to send in the session identifier in-band.
One simple way to achieve this is for the client code to send in the session identifier as the first message in response to the open event, and the server code to interpret the first incoming message's content as the session cookie, to set up the appropriate privilege context (or perhaps close the connection if the cookie is unknown or otherwise grants no privileges to its bearer).
Alternatively, if your WebSocket protocol has some sort of structured message infrastructure, you may define a specific message type for passing a session cookie to the server, as well as a matching response type for the server to let the client know what it thinks of the cookie.
It may be tempting to pass the session cookie in an URI parameter, as in ws://example.com/service?SESSION=123456. This can be adequate in prototyping, but it is probably ill-advised in production, since session cookies should generally be treated as more sensitive than it is customary to treat the list of URIs requested from a web server. Passing session cookies in such a way can work in the short term, but may increase the risk of their accidental exposure via, say, careless analytics techniques. This concern could in some other context be alleviated by passing the sensitive identifier in the body of the request (for example, as a so-called POST parameter), but WebSocket open requests can not have a non-empty body.
You can set cookies for a webSocket connection the same way you set regular cookies, with document.cookie = xxxx. All webSocket connections start with an HTTP request (with an upgrade header on it) and the cookies for the domain you are connecting to will be sent with that initial HTTP request to open the webSocket.
So, as long as you are doing the webSocket connection to the same domain as your web page, then you can just set a cookie for that web page and it will be sent with the webSocket connection request. And, as with other cookies, you set a cookie using document.cookie as described here on MDN.

How could authentication work across microservices?

I have two separate Node.js services.
Service A is responsible for authenticating users. If a user successfully logs in, the user is redirected to Service B (hosted on a subdomain of the domain Service A is hosted on).
I am using JWT for authentication.
Question: How can Service B be aware if a user is or is not authenticated?
I imagine one way that Service B could be aware if they are or aren't authenticated is by asking Service A to check the JWT on each Request to Service B. But how is Service A supposed to send the JWT to the client when the client is going to be redirected to a new origin?
Is it safe to do something like:
window.location.href = 'https://b.example.com?jwt=tokenhere'
I don't believe storing the JWT on localStorage since it does not allow cross origin access.
Another bit of detail that the others haven't touched on:
If your clients are using web browsers, and if you want the user to be authenticated with both services without having to re-login (single sign-on)
E.g. if your domain is example.com, service A is at example.com and service B is at b.example.com.
Send login request to authentication service at example.com
Auth service verifies credentials and if they match, sends a cookie back to client with domain set to example.com
Client receives and stores cookie
On all future requests the browser will send the cookie to service A and service B
In service A and service B verify the JWT received in the cookie (see other peoples answers)
This method ONLY works when you're running on same root domain and your authentication service MUST be on the root domain. If you have a different setup I would recommend looking at a single sign-on product (e.g. Central Authentication Service)
Use RSA for your JWT generation. You can make the public key available to all other microservices. If the client uses CORS to talk to different services directly (vs. an API gateway), pack the JWT in the Authorization header as usual.
If your servers share a secret key you can encrypt and decrypt the token on any server. Inside the token you can write who the user is and what permissions he has. Whenever the user wants to perform some restricted action you decrypt the token and check wheter he has permission to do what he wants. Don't forget to give the token a time to live so it won't last forever.
I'm not a js guru so I won't paste code but you should be able to find an example here

Cookies not sent on OPTIONS requests

For an Angular 1 app I am working on, cookie authentication is used. The problem is: when making OPTIONS calls, cookies are not sent and the server tries to redirect user to login again. Just wondering, whose "fault" is it? Server (Azure API Apps) or frontend? If frontend, how do I send cookies on OPTIONS call? I am using augular-resource and have configured it as below:
$httpProvider.defaults.withCredentials = true
The specification says:
Otherwise, make a preflight request. Fetch the request URL from origin source origin using referrer source as override referrer source with the manual redirect flag and the block cookies flag set, using the method OPTIONS, and with the following additional constraints … Exclude user credentials.
and also
The term user credentials for the purposes of this specification means cookies, HTTP authentication, and client-side SSL certificates that would be sent based on the user agent's previous interactions with the origin. Specifically it does not refer to proxy authentication or the Origin header.
So the client should not send cookies, and the server should be able to respond to the preflight request without requiring authentication to take place first.

Categories