Pow provides an excellent authentication solution for Phoenix HTML applications; but what if you are looking to build out an API? Recently a new API guide has been published, and in this post we’ll have a look at how easy it is to use Pow in the context of an API.
I am constantly impressed with
Pow. New features and enhancements are regularily implemented, and a quick glance at the commit history shows it to be a very active project. Like much of the
Pow documentation, the newly added API guide is pretty comprehensive and provides a good description of how to get things up and running. I figured an example of using a
Pow enabled API might be useful however; so even though we’ll largely be following the guide, we’ll add in some extra endpoints to demonstrate
Pow API authentication in action.
Creating our app
For demonstration purposes, we’ll build a very simple REST API. We’ll pretend we are building out a basic (super, super, basic) warehouse inventory system, so we’ll call our application
Let’s create the API!
Y when asked to fetch and install dependencies. Now we’ll change into the application directory and create the database.
We’ll use a Phoenix generator to create a simple
inventory context with associated endpoints for managing the
products in our warehouse.
As per the
mix output, let’s run the migrations…
… and update the routes.
/lib/warehouse_web_api/router.ex …line 8
That’s all we need for our basic application, let’s fire things up and hit a few endpoints.
Interacting with the API
I am going to be using Insomnia to send requests to the API, but you can use which-ever
REST client you prefer.
First off we need to start the server.
Now let’s see if we can create a product.
We’ll create a new request within Insomnia:
We’ll give the request a name of
Create Product; select
POST as the request method; and specify
JSON as the request body format.
We need to set the
localhost:4000/api/products. The request body needs to include a top level
product key, and under that, a
When we send our request via the
Send button we’ll see that a product has been created!
Fantastic! Let’s set-up one more API call in
Insomnia before moving onto getting
Pow up and running. We’ll set-up a call to retrieve all the current products in our warehouse. This will need to be a
We don’t need a body this time, so we set the
localhost:4000/api/products and we’re good to go.
Great, so we have some API endpoints we can play around with, let’s see how to go about securing our API with
Using Pow within our API
The first step is to add
pow to our list of dependencies.
/mix.exs …line 34
Now we need to grab our dependencies via
We now have some configuration to deal with, but first let’s migrate the database to create the
Pow specific tables.
Nice! … onto configuration.
Only a small configuration change is needed, we just need to add a
Pow configuration section to
config.ex. I’ve placed this above the logger configuration section.
That’s it for configuration, now we need some router updates.
There are a few things we need to update with our
router. First off we need to update the existing
api pipeline to include a custom authentication
plug. In this way our authentication rules will be run on every request. We’ll create this
plug after we’re done updating our router.
We also need to add a new
pipeline for routes that require authentication.
api_protected pipeline we’re referencing a custom
error_handler module which we’ll create after finishing the router updates.
As far as the actual routes go, we need to add some
Pow specific routes for user creation and session management.
While we are at it, we’ll also update the routes for our Product API endpoint. We’ll require authentication for any actions on the endpoint other than the listing and viewing of products. We can do this by adding a second
api scope that pipes through both
:api_protected. Note: we could add the
resources "/products", ProductController, only: [:show, :index] routes in the same scope as the
Pow routes. I kind of like keeping the
Pow routes in a seperate scope, but this is strictly a personal preference.
That’s it for the
router, we now need to go about adding our custom authorization plug, error handler and some
Pow specific controllers.
Adding Pow specific plugs and controllers
We need to add an authentication plug, an error handling module and controller modules for both user registration and session management. To accomplish this we’ll be using the code referenced in API guide pretty much verbatim:
Let’s start with the authorization plug.
So a fair bit of code, but it is all pretty self explanatory, basically everything to do with managing sessions and tokens is handled in the plug.
Next we’ll create the error handler.
Simple… when we encounter an unauthenticated request for a protected route, we just return a
401 status code and an error message.
Pow specific controllers.
The registration controller provides a single endpoint for creating new users.
And here is the session controller.
A little more involved, we have 3 functions; one for logging in (create); one for logout (delete); and one for token renewal.
And that’s it! We’re good to go, so let’s see our newly protected API in action.
Checking out the Pow functionality
Let’s fire up the server.
First thing we will do is run our
List Products request from Insomnia… since nothing has changed with this route, everything should still be all good:
Nice, still working!
Let’s try creating another product.
No dice… this is what we are expecting as we’ve protected the create route. We’ll need to be authenticated in order to create a new product.
Before logging in, let’s update the create function. We’re just going to add a debug line to inspect the
current_user value that
Pow makes available to us. Once we log in and create a product we should see a
current_user showing up in the console.
/lib/warehouse_api_web/controllers/product_controller.ex …line 14
Ok, with that out of the way, let’s register a user. We need to create another
POST request, again with a
localhost:4000/api/registration and the following body:
Running the request, gives us a new user:
The registration response also gives us a token we can now use in calling the create product endpoint.
Add a new Authorization
Header to the create product request, and fill in the token value from the registration request.
Now running the create product request succeeds!
Let’s double-check everything by logging out the user, ensuring we can’t create a product and then logging in the user and ensuring product creation is again a-ok.
So first to log out, we need to create a
We need to use a
localhost:4000/api/session and we need to provide the Authorization header. The header value is how
Pow knows which token to invalidate.
Running the endpoint results in an empty response.
What happens now, if we attempt to create a new product?
As expected we now get a
Let’s make sure we can log in.
We need to create a
URL to use is
localhost:4000/api/session, and the JSON body is:
Sending the request yields a new token:
Now if we update the Authorization header value in the create product request, we’ll be able to create products again.
Sweet! If we glance at our console we can see we also have programmatic access to the current user.
This is useful if for instance you’ve set up some roles you need programmatic access to.
That concludes today’s post. Setting up API authentication with
Pow turns out to be pretty easy!
I had previously looked at using
Pow with an API and wasn’t too sure how to go about it, so it’s awesome to see the documentation and guides continue to evolve.
Note: the API guide also includes some example test modules for testing the
session_controller. It’s worth having a glance at them and you’d likely want to include something similar in your project.
Thanks for reading and I hope you enjoyed the post!