BeetleJuice BeetleJuice - 1 year ago 461
TypeScript Question

Websocket, Angular 2 and JSON Web token Authentication

My Angular 2 app (coded in typescript) has a simple authentication scheme:

  • User logs in:

  • Server returns JSON Web Token (JWT)

  • On every API call, the app sends the JWT in the

  • Server validates the JWT and grants access

Now I'd like to add websockets. I'm wondering how to authenticate the user there. Since I don't control which headers are sent to the websocket server (WS), I cannot send the JWT.

My idea so far (not yet implemented):

  • Client opens websocket:
    let sock = new WebSocket('wss://');

  • WS server accepts the handshake without any authentication check. Standard HTTP headers are available at this stage.

  • Client listens to the
    event on the socket. Once the socket is open:

    • client sends a message with

  • WS server expects 1st message on a socket to be of type
    . Once that is received, server reads the payload, validates
    and sets an

    • If validation fails, server disconnects the socket

    • If a client without
      sends any other type of message, server disconnects the socket

2 problems: server resources can be taken up by clients who connect but never send the JWT, and a cleaner solution would block the handshake if the client is not authenticated.

Other ideas:

  • Client could send JWT in the path:
    new WebSocket('wss://<JWT>/')

    • pro: this info is available during the handshake

    • con: the path doesn't seem to be the "appropriate" place for a JWT. Specifically because intermediate proxies and access logs will save the path; When designing the HTTP API I already made the decision not to include the JWT in the url

  • Server could read the client's IP + UserAgent and match against a DB record that was created by the HTTP server when the JWT was issued. Server will then guess who is connecting

    • pro: this info may be available during the handshake (not sure about IP)

    • con: it seems horribly insecure to "guess" that a client should be associated with a JWT when the client never presented it in the first place. It would mean for instance that someone who spoofs the victim's UA and uses the same network (proxy, public wifi, university intranet...) will be able to impersonate the victim.

How do you authenticate clients on websockets? Assume the user already logged in via HTTP and that the Angular 2 app has a JWT token.

Answer Source

I settled on the following protocol:

1. Client logs into the site and receives and authentication token (JSON Web Token)

GET /auth
    user: 'maggie',
    pwd:  'secret'

// response
{ token: '4ad42f...' }

2. Authenticated client requests a websocket connection ticket

GET /ws_ticket
Authorization: Bearer 4ad42f...

// response: single-use ticket (will only pass validation once)
{ ticket: 'd76a55...', expires: 1475406042 }

3. Client opens the websocket, sending the ticket in query param

var socket = new WebSocket('wss://');

4. Websocket server (PHP) then validates the ticket before accepting the handshake

* Receives the URL used to connect to websocket. Return true to admit user,
* false to reject the connection
function acceptConnection($url, &$error){
    $error = null;
    $params = parse_url($url, PHP_URL_QUERY);

    if(empty($params['ticket'])) $error = "Missing ticket";
    elseif(!validateTicket($params['ticket'])) $error = "Invalid ticket";

    return empty($error);

/** Returns true if ticket is valid, never-used, and not expired. */
function validateTicket($ticket){/*...*/}