In this article, we will explore how to build a REST API with authentication using Elixir and JWT (JSON Web Tokens).
Elixir is a functional programming language built on the Erlang VM, known for its scalability and fault-tolerance.
JWT provides a secure way to authenticate and authorize requests in a stateless manner.
Prerequisites
Before diving into building the REST API, make sure you have the following prerequisites installed:
- Elixir: Install the latest version of Elixir from the official Elixir website.
- Phoenix Framework: Phoenix is a web framework for Elixir. Install Phoenix by running the following command:
mix archive.install hex phx_new
- PostgreSQL: We will be using PostgreSQL as our database.
- Install PostgreSQL by following the instructions in the official PostgreSQL documentation.
Setting up the Project
Let's start by creating a new Phoenix project.
Open your terminal and run the following command:
mix phx.new my_api --no-webpack --no-html
This command creates a new Phoenix project called "my_api" without the default JavaScript and HTML setups.
We will only focus on building the REST API with authentication.
Next, navigate to the project directory:
cd my_api
Configuring the Database
Before we can start building our API, we need to configure the database connection. Open the
config/dev.exs
file and update the database configuration with your PostgreSQL credentials:config :my_api, MyApi.Repo, username: "your_username", password: "your_password", database: "my_api_dev", hostname: "localhost", pool_size: 10
Make sure to update the
username
, password
, and database
fields with your own PostgreSQL credentials.Creating the User Model
To handle user authentication, we need to create a User model.
Run the following command in your terminal to generate the User model:
mix phx.gen.schema Accounts.User users name:string email:string password_hash:string
This command generates a User model with the necessary fields for authentication - name, email, and password hash.
Next, apply the database migration to create the users table:
mix ecto.migrate
Implementing JWT Authentication
To implement JWT authentication, we will be using the Guardian library.
Add Guardian as a dependency in your
mix.exs
file:defp deps do [ # Other dependencies {:guardian, "~> 2.0"} ] end
Run the following command to fetch the new dependency:
mix deps.get
Next, let's generate a Guardian configuration file:
mix guardian.init
This command creates a
config/guardian.exs
file with default configuration options.Update the
config/config.exs
file to include the Guardian configuration:config :my_api, MyApiWeb.Guardian, issuer: "my_api", secret_key: "your_secret_key"
Make sure to replace
"your_secret_key"
with your own secret key.Creating the Authentication Controller
Now, let's create a controller responsible for handling authentication.
Run the following command in your terminal:
mix phx.gen.html Accounts Auth auths email:string password:string
This command generates an Auth controller with email and password fields for authentication.
Next, open the
lib/my_api_web/router.ex
file and add the following route:scope "/api", MyApiWeb do pipe_through :api resources "/auth", AuthController, only: [:create] end
Implementing Authentication Logic
Open the
lib/my_api_web/controllers/auth_controller.ex
file and update the create
action as follows:def create(conn, %{"auth" => %{"email" => email, "password" => password}}) do case Accounts.authenticate(email, password) do {:ok, user} -> token = Guardian.encode_and_sign(user, :token) conn |> put_status(:created) |> render("token.json", token: token) {:error, _reason} -> conn |> put_status(:unauthorized) |> render("error.json", message: "Invalid email or password") end end
This code authenticates the user with the provided email and password.
If the authentication is successful, it generates a JWT token using Guardian and returns it in the response.
Otherwise, it returns an unauthorized error message.
Protecting Routes with JWT
To protect certain routes with JWT authentication, we can create a plug.
Run the following command to generate a
lib/my_api_web/plugs/authenticate.ex
file:mix phx.gen.plug Accounts Authenticate authenticate
Open the
lib/my_api_web/plugs/authenticate.ex
file and update it as follows:defmodule MyApiWeb.Plugs.Authenticate do @behaviour Plug def init(_options), do: [] def call(conn, _options) do case Guardian.Plug.current_resource(conn) do {:ok, _user} -> conn _ -> conn |> put_status(:unauthorized) |> render("error.json", message: "Unauthorized") end end end
This plug checks if the current request is authenticated using Guardian.
If the request is authenticated, it proceeds to the next plug or controller.
Otherwise, it returns an unauthorized error message.
Protecting Routes with JWT
To protect certain routes with JWT authentication, we can create a plug.
Run the following command to generate a
lib/my_api_web/plugs/authenticate.ex
file:mix phx.gen.plug Accounts Authenticate authenticate
Open the
lib/my_api_web/plugs/authenticate.ex
file and update it as follows:defmodule MyApiWeb.Plugs.Authenticate do @behaviour Plug def init(_options), do: [] def call(conn, _options) do case Guardian.Plug.current_resource(conn) do {:ok, _user} -> conn _ -> conn |> put_status(:unauthorized) |> render("error.json", message: "Unauthorized") end end end
This plug checks if the current request is authenticated using Guardian.
If the request is authenticated, it proceeds to the next plug or controller.
Otherwise, it returns an unauthorized error message.
Conclusion
In this article, we have learned how to build a REST API with authentication using Elixir and JWT.
We covered the necessary steps to configure the project, create a user model, implement JWT authentication, and protect routes using plugs.
Using these techniques, you can build secure and scalable REST APIs with Elixir.