Channels. What strategy when connection is denied

There are two different scenario’s I’m trying to handle with the Phoenix JS Websocket client - both produce the same result but I need to handle them both differently.

1. The application is not accessible/responding.
2. Denying a user connection due to failing authentication.

I am authenticating and then denying connection as per the comments for UserSocket.connect/2:

# To deny connection, return `:error`.

1 socket.onError() fires and the socket tries to reconnect again. This is the expected behaviour and is what would be wanted in this scenario.

2 socket.onError() fires and the socket tries to reconnect again. This is not the behaviour I would expect or want. If connection is denied due to failing authentication, then I would not want the socket to keep trying to connect.

If I call socket.disconnect() when the connection is denied, then this stops the repeated attempts to connect again (which seems strange as the socket hasn’t connected yet). This is fine for 2, but not what I would want for 1.


So how do you differentiate between to the two scenarios? Both scenarios fire the same callback function but with no useful data to know which scenario is being responded to?

Or am I looking at this all wrong?

Thanks

2 Likes

Afaik phoenix channels just build on top of the browsers websocket functionality and based on the event given to the onClose callbacks it seems the websocket connection doesn’t hold data to keep both cases apart. So there might not really be much phoenix can do about it.

for technical/specification reasons - you cant know if the onError is called due to wrong auth… what I do is make an ajax call to “auth_check” and if that fails with 401 - I disconnect, clear the bad token, and navigate to the appropriate place (most likely login screen…) - in any other case I let the reconnect run (as it’s most likely lack of connection or server down that is the issue then)

1 Like

Thanks, I’m using ajax as you suggested to determine the reason for onError, this at least allows me to report the correct state back to the user.

My only remaining problem is that I can’t seem to stop the socket from trying to reconnect all the time when auth fails. socket.disconnect() has no effect - presumably because the socket hasn’t opened as a result of the failed auth in UserSocket.connect/2.

Is it even possible to stop the socket from trying to reconnect all the time?

What if you accept the connection anyway and join a personal management channel, where then you send back a response that tells the client it were unauthorized and not allowed to use the connection, then on the client you can safely shut down the connection.

The problem with this though is, if you have a misbehaving client, the connection will be lingering open… But is this worse than a client that steadily tries to reconnect even if it is not authorized?

Yes, thanks, that’s what I’ve been doing - allowing the socket to connect then handling the auth on the channel.

However, reading the comments in UserSocket, I wanted to try setting it up the ‘phoenix way’ handling the auth in connect/2, but if I can’t stop the socket from trying to reconnect, I’ll probably go back to handling the auth on the channel. I guess I’d rather have the odd connection lingering open than the client trying to reconnect all the time.

Thanks for the input.

@phollyer have you tried to set the socket to null once you’re sure you can’t reconnect? And then setting it up again once you login (or wtv)?

1 Like

Hadn’t, but have now, and the socket still tries to reconnect.

I’ve done some digging around in the socket object though and the following stops the socket from trying to reconnect:

socket.reconnectTimer = {scheduleTimeout: function() { return null }}
import {Socket} from "phoenix"

let socket = new Socket("/socket", {params: {token: window.userToken}})

let cancelTimeout = true
function openListener(_event) {
  // once successfully opened
  // try to reconnect
  cancelTimeout = false
}
function closeListener(_event) {
  if(cancelTimeout) {
    // don't retry as it is likely a failure to authenticate
    socket.reconnectTimer.reset() // cancel automatic reconnect
  }
}
socket.onOpen(openListener) 
socket.onClose(closeListener) 
socket.connect()

Unfortunately the WebSocket doesn’t have access to the 403 status code and the CloseEvent code always seems to be 1006 “Abnormal Closure”.

5 Likes

Thanks, makes perfect sense. Hadn’t thought of it that way, such a simple solution. :+1:

1 Like