coder/websocket

403 when connecting through js websocket. Works when testing with Postman

JPTomorrow opened this issue · 6 comments

I am having an issue where it client browsers throw a 403 forbidden error when trying to connect.

Firefox Console

Client Side Code:

`
import { FormEvent, useCallback, useEffect, useState } from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import "./App.css";

type Message = {
id: number;
name: string;
msg: string;
};

function App() {
const [isCustomer, setIsCustomer] = useState(false);
const [isSelected, seIsSelected] = useState(false);
const myCustomerId = 12121212;
const mySupportRepId = 42424242;

const getSocketUrl = useCallback(() => {
  return new Promise<string>((resolve) => {
    setTimeout(() => {
      console.log("refresh URL...");
      resolve(
        `ws://${window.location.hostname}:8080/${
          isCustomer ? "customer" : "support"
        }-connect`
      );
    }, 2000);
  });
}, [isCustomer]);

const ws = useWebSocket(getSocketUrl, {
  protocols: ["echo-protocol"],
  queryParams: {
    id: isCustomer ? myCustomerId : mySupportRepId,
  },
  onError(event: WebSocketEventMap["error"]) {
    console.log(event);
  },
});

const [msgs, setMsgs] = useState<Message[]>([
  {
    id: myCustomerId,
    name: "Justin Morrow",
    msg: "Hello, I'm having trouble with my account.",
  },
  {
    id: mySupportRepId,
    name: "Random Support Rep",
    msg: "I'm sorry to hear that. I'll do my best to help you.",
  },
  {
    id: myCustomerId,
    name: "Justin Morrow",
    msg: "Thank you!",
  },
  { id: mySupportRepId, name: "Random Support Rep", msg: "You're welcome!" },
]);

useEffect(() => {
  if (ws.lastJsonMessage !== null) {
    setMsgs((prev) => [...prev, ws.lastJsonMessage]);
  }
}, [ws.lastJsonMessage]);

const handleSubmit = (e: FormEvent<HTMLInputElement>) => {
  const i = e.target as HTMLInputElement;
  const msg = i.value;
  i.value = "";
  const data = {
    id: isCustomer ? myCustomerId : mySupportRepId,
    msg: msg,
  };
  ws.sendJsonMessage(data);
};

return (
  <>
    <div
      className={`absolute top-0 left-0 w-screen h-screen flex flex-col items-center justify-center bg-gradient-to-b from-gray-400 to-black gap-5 ${
        isSelected ? "hidden" : ""
      }`}
    >
      <button
        className="bg-green-500 transition-transform hover:scale-105"
        onClick={() => {
          setIsCustomer(true);
          seIsSelected(true);
        }}
      >
        <h1 className="text-black">Connect as Customer</h1>
      </button>
      <button
        className="bg-red-700 transition-transform hover:scale-105"
        onClick={() => {
          setIsCustomer(false);
          seIsSelected(true);
        }}
      >
        <h1>Connect as Support Representative</h1>
      </button>
    </div>
    <div className="flex flex-col gap-5 bg-purple-900/20 border-[1px] border-white/20 rounded-3xl max-w-[700px] p-12">
      <h1 className="text-white text-3xl underline underline-offset-4 uppercase">
        chatting as {isCustomer ? "customer" : "support"}
      </h1>
      <p className="uppercase text-2xl text-white">
        ready state: {ws.readyState}
      </p>
      <div className="flex flex-col gap-5 border-b-[1px] border-white">
        <div className="w-full h-full flex flex-col gap-5 overflow-auto max-h-[300px] px-3 pb-8">
          {msgs.map((msg, i) => {
            return (
              <div key={i} className="flex flex-col gap-5">
                <h1
                  className={`text-white text-xl rounded-xl p-5 w-fit ${
                    msg.id == myCustomerId
                      ? "text-right bg-gray-800 float-start"
                      : ""
                  } ${
                    msg.id == mySupportRepId
                      ? "text-left bg-gray-500 float-end"
                      : ""
                  }`}
                >
                  <p className="text-sm text-left underline">{msg.name}</p>
                  {msg.msg}
                </h1>
              </div>
            );
          })}
        </div>
      </div>
      <input
        onSubmit={handleSubmit}
        className="border-[1px] border-white/30 px-5 py-3 rounded-xl bg-transparent"
        type="text"
      />
    </div>
  </>
);

}

export default App;

`

Just a guess but have you tried setting w.Header().Set("Access-Control-Allow-Origin", "*") on the server side? This seems like a likely CORS issue.

Yes, I set that header. It changed nothing it seems.

image

@JPTomorrow not having seen any of the server code, I can only suggest you try with proper CORS handling, e.g. via https://github.com/rs/cors or https://github.com/go-chi/cors. Other than that I don’t think there’s a whole lot I can do to help. The issue seems to either be a misbehaving client or misconfigured server. Or perhaps you’re opening a local .html file in which case I’d recommend to serve it behind a web server.

Oh and in case you hadn't already, do have a look at these options too:

websocket/accept.go

Lines 30 to 52 in 3dd723a

// InsecureSkipVerify is used to disable Accept's origin verification behaviour.
//
// You probably want to use OriginPatterns instead.
InsecureSkipVerify bool
// OriginPatterns lists the host patterns for authorized origins.
// The request host is always authorized.
// Use this to enable cross origin WebSockets.
//
// i.e javascript running on example.com wants to access a WebSocket server at chat.example.com.
// In such a case, example.com is the origin and chat.example.com is the request host.
// One would set this field to []string{"example.com"} to authorize example.com to connect.
//
// Each pattern is matched case insensitively against the request origin host
// with path.Match.
// See https://golang.org/pkg/path/#Match
//
// Please ensure you understand the ramifications of enabling this.
// If used incorrectly your WebSocket server will be open to CSRF attacks.
//
// Do not use * as a pattern to allow any origin, prefer to use InsecureSkipVerify instead
// to bring attention to the danger of such a setting.
OriginPatterns []string

I had this same issue. Setting OriginPatterns worked for me.

Thanks for confirming @FelicianoTech. I'm closing this issue as I don't see anything actionable here for us to do. Feel free to re-open if I missed something.