Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
JPTomorrow opened this issue Sep 4, 2024 · 5 comments

Comments

@JPTomorrow
Copy link

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;

`

@mafredri
Copy link
Member

mafredri commented Sep 4, 2024

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.

@JPTomorrow
Copy link
Author

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

image

@mafredri
Copy link
Member

mafredri commented Sep 4, 2024

@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.

@mafredri
Copy link
Member

mafredri commented Sep 4, 2024

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

@FelicianoTech
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants