How to send message back to client when authorize failed

I do the authorize in y-websocket like this:


#!/usr/bin/env node

/**
 * @type {any}
 */
const WebSocket = require('ws')
const http = require('http')
const jwt = require('jsonwebtoken')
const wss = new WebSocket.Server({ noServer: true })
const setupWSConnection = require('./utils.js').setupWSConnection

const host = process.env.HOST || '0.0.0.0'
const port = process.env.PORT || 1234
const JWT_SIGN_KEY = process.env.JWT_SIGN_KEY || 'key-missing'

const server = http.createServer((request, response) => {
  if (request.url === '/healthz') {
    response.writeHead(200, { 'Content-Type': 'text/plain' })
    response.end('ok')
  } else {
    response.writeHead(200, { 'Content-Type': 'text/plain' })
    response.end('okay')
  }
})

wss.on('connection', setupWSConnection)

server.on('upgrade', (request, socket, head) => {
  // You may check auth of request here..
  // See https://github.com/websockets/ws#client-authentication
  /**
   * @param {any} ws
   */
  const handleAuth = ws => {
    const url = new URL(request.url, 'wss://ws.example.top')
    console.log(url.pathname)
    if (request.url !== '/healthz') {
      // https://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#query-param
      const token = url.searchParams.get('access_token')
      if (!token) {
        socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
        socket.destroy()
        return
      }
      try {
        jwt.verify(token, JWT_SIGN_KEY)
        wss.emit('connection', ws, request)
      } catch (err) {
        console.error('error:' + err)
        socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
        socket.destroy()
      }
    }
  }
  wss.handleUpgrade(request, socket, head, handleAuth)
})

server.listen(port, host, () => {
  console.log(`running at '${host}' on port ${port}`)
})

when the token verify failed, the client throw error invalid frame header.

CollarCodeEditor-07009f38.js:13 WebSocket connection to 'wss://ws.exmaple.top/a5553fe26f47445986cc7e19e3fe94cc?access_token=eyJhbGciOiJIUzUxSWQiOjEwMywiZGV2aWNlSWQiOiI3NzkwMGQzYzQxYmI5NjYxOTRmZDhmZGQwVdTMSIsImV0IjowLCJwaWQiOjEzLCJleHAiOjE2OTUxMTE0ODl9.WYe1cguzwWJivPOn1tgJbEftfcir45N34MC0WwWpW6MBFCSTHcYTmzMzoGzsLqpeJ5klQVVO6nPNq3ia5BGYxQ' failed: Invalid frame header

what should I do to send back the error info to client? Am I missing something?

I am send message back after the websocket connection like this:

const handleAuth = (request, conn) => {
  const url = new URL(request.url, 'wss://ws.example.top')
  if (request.url !== '/healthz') {
    // https://self-issued.info/docs/draft-ietf-oauth-v2-bearer.html#query-param
    const token = url.searchParams.get('access_token')
    try {
      jwt.verify(token, JWT_SIGN_KEY)
    } catch (err) {
      switch (err.name) {
        case 'TokenExpiredError':
          console.error('expire:' + err)
          conn.close(WEBSOCKET_AUTH_TOKEN_EXPIRE)
          break
        case 'JsonWebTokenError':
          console.error('error:' + err)
          conn.close(WEBSOCKET_AUTH_FAILED)
          break
      }
    }
  }
}

invoke this function in setupWSConnection method. On the client, we could receive the code like this:

// Do not reconnect if auth failed
      if (event.code === 4000) {
        console.log('Auth failed', event.code)
        provider.emit('auth', [{
          status: 'failed'
        }])
        return
      }

      if (event.code === 4001) {
        console.log('Auth failed expire', event.code)
        provider.emit('auth', [{
          status: 'expired'
        }])
        return
      }

more detail information: Authentication testing · Issue #7 · yjs/y-websocket · GitHub