Json Web Tokens (Jwts) are a rich alternative to cookies for ensuring authenticated access to protected assets, and it is now easy to implement them in a Phoenix-Elm (i.e. single page) environment using the Guardian and (my own) Elm-Jwt libraries respectively. This blog accompanies the example code in my Github repo, and steals gratefully from Daniel Neighman’s post on Guardian for APIs.
Setting up
On the Phoenix side you will need to add a module for handling passwords - comeonin seems to be the obvious choice - and Guardian.
The Elm package manager is your friend on the client side - just elm package install -y simonh1000/elm-jwt
.
Guardian needs some configuration
Issuing tokens
I won’t cover registering users here, but once they existing in the system they will need to login in via a route we shall call sessions
. (mix phoenix.gen.json Session sessions
)
The client will be sending credentials via a POST request, and our controller (below) will prepare the token to return.
Auth.login_by_username_and_pass
checks the credentials and, if the password is validated, assigns the user details to :current_user
. Guardian.Plug.api_sign_in
uses that information to construct the token with the help of my custom serializer.
Guardian takes the output of the for_token
serializer and adds it as the “username” in the body of the token. The full token is eventually sent to the client by rendering in the view as a Json object {"token" : token}
.
Turning to the client, a simple form collects a username and password and sends them to the server as part of the update function, and with the help of elm-jwt’s authenticate
function. Using the new 0.17 syntax, this reads as:
Authenticate sends the credentials as a POST and extracts the token returned using the provided decoder. Http and token decoding errors are returned to the user as a LoginFail message. On LoginSuccess
the token needs to be stored in the model.
I also generally use a port
to save it in localstorage, and seek to load the token form local storage (together with a timestamp) when the page loads. Before using it, I can then check if it is still valid:
Making authenticated requests
Once the token is available, you can use it with the GET replacement function provided in the library, Jwt.get
, which creates a custom GET request that attaches the token to the Http headers before sending. For example we might have this in update
:
Note the optional use of promote401
, which is an additional convenience function that isolates Unauthorized responses from the server and checks whether these are causes by the token having expired. We could use this in our update
function as:
Back on the server side, we need to look for the token, load the user details associated with it, and add it to the connection. To start we add a new pipeline to our router.ex and apply it to all api requests.
It is in the controller that we test that the user is in fact authenticated before deciding whether to provide a result. Guardian does that and has a helper to provide access to the user details, which I am passing to the view to use to customise the response.
Jwt’s are growing in popularity especially for single page apps. They can now be used easily and comfortably with your favourite functional client-server combination. Enjoy!