49

The upgrade request for opening a websocket connection is a standard HTTP request. On the server side, I can authenticate the request like any other. In my case, I would like to use Bearer authentication. Unfortunately, there is no way to specify headers when opening a websocket connection in the browser, which would lead me to believe that it's impossible to use bearer authentication to authenticate a web socket upgrade request. So -- Am I missing something, or is it really impossible? If it is impossible, is this by design, or is this a blatant oversight in the browser implementation of the websocket API?

w.brian
  • 16,296
  • 14
  • 69
  • 118
  • following the answers to your question, do i understand correct that this isn't possible? What did you end up doing? Tnx for sharing! – Imnotapotato Aug 02 '23 at 17:20

3 Answers3

62

The API allows you to set exactly one header, namely Sec-WebSocket-Protocol, i.e. the application specific subprotocol. You could use this header for passing the bearer token. For example:

new WebSocket("ws://www.example.com/socketserver", ["access_token", "3gn11Ft0Me8lkqqW2/5uFQ="]);

The server is expected to accept one of the protocols, so for the example above, you can just validate the token and respond with header Sec-WebSocket-Protocol=access_token.

Kalle
  • 2,157
  • 1
  • 22
  • 21
  • I used this approach, except I concatenated the token and value in a single "protocol" value. Thanks! – dmansfield Jul 15 '16 at 13:43
  • have you a solution use `Authorization` header instand of `Sec-WebSocket-Protocol` header? – Stéphane GRILLON Jun 28 '18 at 16:15
  • no, but it's probably easier for the server to read the access_token as the bearer token in case the client is JS (yeah pretty stupid). – Astronaut Jul 11 '18 at 14:24
  • Is it possible to set a cookie for your websocket connection, and use that to pass along any custom auth headers? – jayongg Feb 05 '19 at 03:13
  • 2
    I guess it depends on your viewpoint whether it's a feature or a hack, but it's meant as an *application specific* subprotocol, so the application can request and use the headers however it likes. – Kalle Sep 25 '19 at 10:16
  • Is this still the best way to handle this? – pfcodes Nov 09 '19 at 03:04
  • 1
    Yes, it's still the best way. Given that this is in the spec it won't change quickly and I'm not aware of any standardization efforts to allow general access to websocket request headers from js. Also, to reply to @jayongg, it's possible to set cookies and they are sent with the ws upgrade request. – Kalle Nov 09 '19 at 18:10
  • What about security here? Where will you get the token? If from cookie and pass it in javascript, then it has to be readable and it's not secure. If the page will be generated with this exact line and token can be used just once, then it's ok. But what to do when connection is lost and you need to pass to token again? – martin.malek Jul 16 '21 at 11:31
  • @martin.malek - security wise, it's no more or no less secure than a session cookie. If you have a session, then just use that. To me, this is about the (in-)ability to set custom auth headers via the websocket JS API, which you might be interested in when you are *not* using a session. – Kalle Jul 16 '21 at 12:06
  • I'm not a security expert, but correct me if I'm wrong - having the token like that in the code, means that the token doesn't get sent encrypted, which doesn't differ much from the previous comment by @Murali S https://stackoverflow.com/a/60817734/3802177 – Imnotapotato Aug 02 '23 at 17:13
23

You are right, it is impossible for now to use Authentication header, because of the design of Javascript WebSocket API. More information can be found in this thread: HTTP headers in Websockets client API

However, Bearer authentication type allows a request parameter named "access_token": http://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#query-param This method is compatible with websocket connection.

Community
  • 1
  • 1
Romain F.
  • 766
  • 10
  • 15
  • 9
    The documentation tells you its insecure and to not to use it unless absolutely neccessary. – Jeremy Apr 09 '20 at 04:46
-2

Example for basic authentication using token servlet http request header before websocket connection:

****ws://localhost:8081/remoteservice/id?access_token=tokenValue****

verify your token return true if valid else return false

endpoint configuration:

@Configuration
@EnableWebSocket
public class WebSocketConfiguration implements WebSocketConfigurer{

    @Autowired
    RemoteServiceHandler rsHandler;

public void registerWebSocketHandlers(WebSocketHandlerRegistry registry){
        registry.addHandler(rsHandler, "/remoteservice/{vin}").setAllowedOrigins("*").addInterceptors(new HttpHandshakeInterceptor());
    }   
}

validate the token before established websocket connectin:

public class HttpHandshakeInterceptor implements HandshakeInterceptor{

@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,  Map attributes) throws Exception 
{
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
String token = servletRequest.getServletRequest().getHeader("access_token");
try {
            Claims claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();

            if (claims!=null) {
                return true;
            }
        } catch (Exception e) {

            return false;
        }
        return false;
}

skip the http security endpoint

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

    @Override 
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().anyRequest(); 
    }

}

pom.xml

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

add the request header in js file as you like

var request = URLRequest(url: URL(string: "ws://localhost:8081/remoteservice")!)
request.timeoutInterval = 5 // Sets the timeout for the connection
request.setValue("someother protocols", forHTTPHeaderField: "Sec-WebSocket-Protocol")
request.setValue("14", forHTTPHeaderField: "Sec-WebSocket-Version")
request.setValue("chat,superchat", forHTTPHeaderField: "Sec-WebSocket-Protocol")
request.setValue("Everything is Awesome!", forHTTPHeaderField: "My-Awesome-Header")
let socket = WebSocket(request: request)
Murali S
  • 17
  • 1