Need Help: Step by step on on sending PHP Token to Phoenix, Authentication & Bearer Token usage

Background: I have a PHP/MySQL website set up long ago before Phoenix. The user does their username/password login on our /login.php page. Upon successful login, my site generates a random “login token”, which gets stored as an httpOnly cookie in their browser and also gets saved in the database. When the PHP session expires, we just look up the login token in the database and we know it’s the user renewing their PHP session, and we assign a new login token.

In regards to Phoenix, the login auth is already done on my PHP script, and I receive a PHP-generated auth token stored as a cookie. Based on advice here, it sounds like I will be using Phoenix as an API, since all the front-end stuff is done using my PHP website.

In regards to a strategy, I received this advice, which sounds promising:

So how exactly would I do this? Am I supposed to use CURL in my PHP script and send a POST request to Phoenix with the “login token” as a parameter, and then Phoenix looks up MySQL once more to authenticate on it’s end?

What would the Plug and Endpoint code look like in Phoenix for this?

I’m assuming I will use something like: Phoenix.Token — Phoenix v1.7.10 “Phoenix.Token.sign”, to create this “Bearer Token” and that gets sent back to my PHP login.php script via the CURL response?

What should my PHP script do when it receives this Bearer Token in the CURL response? Does that Bearer Token get stored as an httpOnly cookie before redirecting the user to whatever page they are browsing?

When the user is browsing through different pages and connects to Phoenix on each page load, do I use something like “Plug.Conn.get_req_header(conn, “cookie[bearer-token]”)” on each connection to fetch that token and run Phoenix.Token.verify on it to authenticate the user without having to do database calls on each connection?

Again, what would the full Plug/Endpoint look like? I ask because I cannot find any simple examples anywhere for my specific case. Most examples have the login/registration page all built into Phoenix.

I’m very gradually understanding this through much reading and by finding bits and pieces all around, but I want to make sure I’m on the right path.

No, you can do it, but it would be a waste to hit the db, you send a POST request to phoenix with something that identifies the user, be it username, email, login_token, whatever and sign it (in the example that you linked he sets as user_id = 1) and return the token to the php script.

# Creating a token passing a username and a user_id with the value of "my_id"
iex(1)> t = Phoenix.Token.sign(MyappWeb.Endpoint, "user_auth", ["my_username", "my_id"])
"SFMyNTY.g2gDbAAAAAJtAAAAC215X3VzZXJuYW1lbQAAAAVteV9pZGpuBgDkwvLHewFiAAFRgA.dwARZRk7f0tZDDR-f5orRYPdtYKW6oj3z9P_sZdxDGI"

# As you can see, you can retrieve the data when verifying
iex(2)> Phoenix.Token.verify(MyappWeb.Endpoint, "user_auth", t)
{:ok, ["my_username", "my_id"]}

# The reason for the signing is to prevent tampering, just changed the last letter and now it is invalid
iex(3)> t2 = "SFMyNTY.g2gDbAAAAAJtAAAAC215X3VzZXJuYW1lbQAAAAVteV9pZGpuBgDkwvLHewFiAAFRgA.dwARZRk7f0tZDDR-f5orRYPdtYKW6oj3z9P_sZdxDGa"
"SFMyNTY.g2gDbAAAAAJtAAAAC215X3VzZXJuYW1lbQAAAAVteV9pZGpuBgDkwvLHewFiAAFRgA.dwARZRk7f0tZDDR-f5orRYPdtYKW6oj3z9P_sZdxDGa"
iex(4)> Phoenix.Token.verify(MyappWeb.Endpoint, "user_auth", t2)
{:error, :invalid}

When your php script receives the token it should store it somewhere where it can be referenced in pages across your app, you can persist it to a database, a cookie, localstorage, redis, it doesn’t matter as long as you can pass it to your app with <?= chat_user_token ?> when he goes to a page where he will connect to the channel.

Also, for security reasons tokens should be ephemeral, the Phoenix.Token page suggests giving a max_age of 1 day, no need to worry too much when you’re experimenting with it, but keep in mind that it is a thing that you will probably have to do, as for the random login_token that your site generates, it can be used to sign a token, but the token that you will be using to connect will be created by phoenix and there’s no need for any kind of database at all for a simple implementation.

And in your app site, I don’t know how you connect to the actual chat server, socket.io, sockjs or pure javascript, but phoenix have a peculiar implementation that you can find in the section “Client Libraries” here, it isn’t complicated to implement it by yourself, but it takes time, so it is better to install the official one from npm for now.

In the official library docs you can see the params being passed when setting up the socket.

let socket = new Socket("/socket", {params: {userToken: "<?= chat_user_token ?>"}})

The rest of the work is mostly setting up what you’ll find in this session and verifying the token when joining a room.

Hope it helps.

2 Likes