km751
Problem with authenticating WebSockets
I have a problem with authenticating WebSocket connection.
I use Phoenix and Vue + phoenix-socket as my front-end, and everything was fine before adding authentication.
The connection can be established and data is transferred properly but after that, it starts sending errors and I don’t understand why.
My browser console looks like this:
receive: ok feed:1 phx_reply (1) {response: {…}, status: "ok"}
Joined successfully {feed: Array(3)}
WebSocket connection to 'ws://localhost:4000/socket/websocket?vsn=1.0.0' failed: Error during WebSocket handshake: Unexpected response code: 403
push: phoenix heartbeat (2) {}
receive: ok phoenix phx_reply (2) {response: {…}, status: "ok"}
WebSocket connection to 'ws://localhost:4000/socket/websocket?vsn=1.0.0' failed: Error during WebSocket handshake: Unexpected response code: 403
Phoenix:
[info] CONNECTED TO TweeterApiWeb.UserSocket in 0┬Ás
Transport: :websocket
Serializer: Phoenix.Socket.V1.JSONSerializer
Parameters: %{"token" => "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJ0d2VldGVyX2FwaSIsImV4cCI6MTU5NjkxMjAxNiwiaWF0IjoxNTk0NDkyODE2LCJpc3MiOiJ0d2VldGVyX2FwaSIsImp0aSI6IjViYWFlMDRlLTBjMTYtNDEyMi05Y2VlLWZmMzQ2OWM1YWE1YiIsIm5iZiI6MTU5NDQ5MjgxNSwic3ViIjoiMSIsInR5cCI6ImFjY2VzcyJ9.-ZJMyyEBKd0_nHYUBGdaI0qdHn1nuWtpG8sEUHqikBuWTB2sKw9Sk36OsUpXBS5ozRpe2l2VXq8NI58HydIhZA", "vsn" => "1.0.0"}
[debug] QUERY OK source="tweets" db=0.0ms idle=875.0ms
SELECT t0."id", t0."content", t0."comment_count", t0."retweet_count", t0."like_count", t0."profile_id", t0."inserted_at", t0."updated_at" FROM "tweets" AS t0 WHERE (t0."profile_id" = $1) [1]
[info] JOINED feed:1 in 0┬Ás
Parameters: %{}
[info] REFUSED CONNECTION TO TweeterApiWeb.UserSocket in 0┬Ás
Transport: :websocket
Serializer: Phoenix.Socket.V1.JSONSerializer
Parameters: %{"vsn" => "1.0.0"}
It looks like the connection is refused because there is no token in params but I only check the authentication when the socket connects, so the token should be completely unnecessary once the connection is established, right?
Here is my user_socket.ex:
defmodule TweeterApiWeb.UserSocket do
use Phoenix.Socket
alias TweeterApi.Accounts
alias TweeterApi.Accounts.Guardian
## Channels
channel "feed:*", TweeterApiWeb.FeedChannel
@impl true
def connect(%{"token" => token}, socket, _connect_info) do
case Guardian.resource_from_token(token) do
{:ok, user, _claims} ->
current_profile = Accounts.get_profile_by(user_id: user.id)
{:ok, assign(socket, :current_profile_id, current_profile.id)}
{:error, _reason} ->
:error
end
end
def connect(_params, _socket, _connect_info), do: :error
@impl true
def id(socket), do: "users_socket:#{socket.assigns.current_profile_id}"
end
The channel code:
defmodule TweeterApiWeb.FeedChannel do
use TweeterApiWeb, :channel
alias TweeterApi.Tweets
def join("feed:" <> current_profile_id, _params, socket) do
if String.to_integer(current_profile_id) === socket.assigns.current_profile_id do
current_profile_tweets = Tweets.list_profile_tweets(current_profile_id)
response = %{
feed:
Phoenix.View.render_many(current_profile_tweets, TweeterApiWeb.TweetView, "tweet.json")
}
{:ok, response, socket}
else
{:error, %{reason: "Not authorized"}}
end
end
def terminate(_reason, socket) do
{:ok, socket}
end
end
and Vue.js code:
<script>
import UserProfileSection from '@/components/sections/UserProfileSection.vue'
import TimelineSection from '@/components/sections/TimelineSection.vue'
import FollowPropositionsSection from '@/components/sections/FollowPropositionsSection.vue'
import NewTweetForm from '@/components/sections/NewTweetForm.vue'
import { mapGetters } from 'vuex'
import { Socket } from 'phoenix-socket'
export default {
name: 'AppFeed',
components: {
UserProfileSection,
TimelineSection,
FollowPropositionsSection,
NewTweetForm,
},
data() {
return {
tweets: [],
}
},
computed: {
...mapGetters('auth', ['currentProfileId', 'token'])
},
mounted() {
const WEBSOCKET_URL = 'ws://localhost:4000'
const socket = new Socket(`${WEBSOCKET_URL}/socket`, {
params: { token: this.token },
logger: (kind, msg, data) => {
console.log(`${kind}: ${msg}`, data)
},
})
socket.connect()
this.channel = socket.channel('feed:' + this.currentProfileId, {})
this.channel
.join()
.receive('ok', (resp) => {
console.log('Joined successfully', resp)
console.log(resp)
this.tweets = resp.feed
})
.receive('error', (resp) => {
console.log('Unable to join', resp)
})
}
}
</script>
Marked As Solved
sb8244
What does the web request that the JavaScript makes to initiate the WebSocket look like? It will include token as a GET parameter.
Your initial request makes it look like token is being passed up, but then a second request is made that fails. Do you have 2 Socket connections by chance?
Why does "token" => "eyJhbGciOiJIUzU..." differ from the second call? Why are there 2 calls at all?
Few high level thoughts are that you are on an old serializer version (1 instead of 2) and using phoenix-socket library. I would suggest making sure you’re on the latest versions possible—in general a good thing—although that is likely not the actual issue at hand.
edit: Have you tried console.log(this.token) in the mounted function? What if it is being mounted without a token?
Also Liked
kokolegorille
Hello and welcome…
It seems You are using an outdated library.
This one should be better
https://www.npmjs.com/package/phoenix
BTW You did not mention your phoenix version ![]()
km751
I had a second socket connection in my vuex module used in one of the child components.
Thank you for your help!
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









