How to Authenticate your Elixir/Phoenix APIs using Guardian

Building secure, authenticated APIs is a critical part of modern web development. As an Elixir developer, you have some great tools at your disposal to implement API authentication quickly and easily in your Phoenix applications. In this guide, we‘ll walk through the process of adding token-based authentication to a Phoenix API using the Guardian library.

Why is API Authentication Important?

APIs often provide access to sensitive user data and actions, so it‘s crucial to ensure only authorized users and clients can access protected resources. Requiring authentication for your API endpoints helps:

  • Safeguard user privacy by restricting access to authorized parties
  • Prevent data breaches and maintain security of your systems
  • Control usage and prevent API abuse or unintended uses
  • Provide personalized, user-specific data and functionality

While there are several approaches to API authentication, using tokens is a popular and effective method. The general idea is that upon successful login, the server generates a unique access token which is then included by the client with each subsequent request to authenticate.

Authentication Libraries in Elixir

To add API authentication in Phoenix, we‘ll use a pair of powerful Elixir libraries:

  • Ueberauth – Allows users to authenticate with your Elixir app using external identity providers like Google, Facebook, Twitter etc.

  • Guardian – Provides a full token-based authentication solution, handling things like token generation, session storage, and permission management.

Together, Ueberauth and Guardian make it straightforward to implement secure, standards-based authentication in your Elixir APIs. Ueberauth provides an easy way for users to login or signup, while Guardian takes care of managing the session and authentication status.

Setting Up Dependencies

To get started, let‘s add the necessary dependencies to our Phoenix application. Open up your mix.exs file and add ueberauth, guardian and jsonapi to the deps:

defp deps do
  [
    # ...
    {:ueberauth, "~> 0.4"},
    {:guardian, "~> 1.0"},
    {:jsonapi, "~> 0.7.0"},
  ]  
end

We‘ll also add ueberauth to the list of our app‘s dependencies:

def application do
  [
    extra_applications: [:logger, :runtime_tools, :ueberauth]    
  ]
end

Now, run mix deps.get to install the new packages.

Configuring Ueberauth and Guardian

Next, we need to configure Ueberauth and Guardian in our app. For this example, we‘ll use Google OAuth for authentication.

Open config/config.exs and add this Ueberauth config:

config :ueberauth, Ueberauth,
  providers: [
    google: {Ueberauth.Strategy.Google, []}
  ]

config :ueberauth, Ueberauth.Strategy.Google.OAuth,
  client_id: System.get_env("GOOGLE_CLIENT_ID"),
  client_secret: System.get_env("GOOGLE_CLIENT_SECRET")

This sets up the Google OAuth strategy and pulls the app credentials from environment variables, which is more secure than hardcoding them.

For Guardian, we‘ll use its default JWT tokens and include some standard config:

config :my_app, MyApp.Guardian,
  issuer: "my_app",
  secret_key: "Secret key. You can use `mix guardian.gen.secret` to get one"

config :my_app, MyApp.AuthPipeline,
  module: Guardian,
  error_handler: MyApp.AuthErrorHandler

Generate a secret key using the mix guardian.gen.secret command as instructed. This key will be used to sign the tokens.

The Authentication Controller

With our auth libraries configured, we can implement the core authentication logic in a dedicated AuthController.

defmodule MyApp.AuthController do
  use MyAppWeb, :controller

  plug Ueberauth

  alias MyApp.{User, UserToken}

  def callback(%{assigns: %{ueberauth_auth: auth}} = conn, _params) do
    case User.find_or_create(auth) do
      {:ok, user} ->
        {:ok, token, _claims} = UserToken.encode_and_sign(user)
        render(conn, "show.json", user: user, token: token)

      {:error, reason} -> 
        render(conn, "error.json", message: reason)
    end
  end

  def callback(%{assigns: %{ueberauth_failure: _fails}} = conn, _params) do
    render(conn, "error.json", message: "Failed to authenticate.")
  end
end

This controller handles the OAuth callback after a user authorizes with Google. It uses the Ueberauth data to either fetch an existing user record or create a new one.

If successful, it generates a JWT token for the user with Guardian which is returned in the response. The token should be included by clients in the Authorization header of subsequent requests to authenticate.

Protecting Routes & Pipelines

To make use of our authentication system, we need to create a pipeline that will enforce authentication for protected routes. In Phoenix, we can define pipelines in the Router.

pipeline :api do
  plug :accepts, ["json"]
end

pipeline :api_auth do
  plug Guardian.Plug.Pipeline, 
    module: MyApp.Guardian,
    error_handler: MyApp.AuthErrorHandler

  plug Guardian.Plug.VerifyHeader
  plug Guardian.Plug.EnsureAuthenticated
  plug Guardian.Plug.LoadResource, allow_blank: true
end

scope "/api", MyApp do
  pipe_through [:api, :api_auth]

  # Protected routes here
end

Our :api_auth pipeline uses several Guardian plugs to ensure requests are authenticated with a valid token. We can apply it to any scopes or routes that require authentication.

Accessing the Current User

Within authenticated routes, you can access the current user through the Guardian.Plug.current_resource/1 function.

For example, to fetch the current user in a controller:

defmodule MyApp.MyController do
  use MyAppWeb, :controller

  def show(conn, _params) do
    user = Guardian.Plug.current_resource(conn)
    render(conn, "show.json", user: user)
  end
end  

By simply adding the :api_auth pipeline to this controller‘s routes, we can ensure only authenticated requests can access it, and easily get the current user making the request.

Testing the Authenticated API

To verify everything is working, we can test our API using a tool like Postman or curl. Start your phoenix server with mix phx.server.

First, make a request to the Ueberauth endpoint to initiate the Google OAuth flow:

GET /auth/google

You should be redirected to Google to authorize the application. Once you approve, you‘ll be redirected back to our AuthController callback, which will respond with the user data and an access token.

{
  "access_token": "eyJhbGciOiJIUzUxNiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 3600,
  "user": {
    "id": 1,
    "email": "[email protected]",
    ...
  }
}

Now, include this token in the Authorization header of API requests:

Authorization: Bearer eyJhbGciOiJIUzUxNiIsInR5cCI6IkpXVCJ9...

Authenticated endpoints should now be accessible, while unauthenticated requests will receive a 401 Unauthorized response.

Security Best Practices

When implementing API authentication, it‘s important to follow security best practices to harden your application. Some key considerations:

  • Keep your secret keys safe and never expose them client-side or in version control. Use environment variables or a secrets manager.
  • Implement cross-site request forgery (CSRF) protection if your app also has a frontend
  • Set an appropriate expiration time on tokens and allow them to be revoked if needed
  • Use HTTPS everywhere to encrypt data in transit and prevent man-in-the-middle attacks
  • Carefully manage your OAuth app credentials and be mindful of the scopes/permissions you request

Advanced Topics & Next Steps

We‘ve covered the essentials of setting up API authentication in Phoenix with Guardian, but there‘s plenty more to dive into!

Some additional topics to explore:

  • Refresh tokens for issuing new access tokens
  • Handling token expiration and revocation
  • User roles and authorization levels
  • Two-factor auth or additional user verification
  • Supporting additional OAuth providers

The Ueberauth and Guardian docs provide many more details on these advanced features and how to customize your app‘s authentication system.

Conclusion

Adding secure authentication to your Phoenix APIs doesn‘t have to be a daunting task. With the help of Elixir packages like Ueberauth and Guardian, we can implement a robust token-based authentication solution without too much hassle.

By simply installing the dependencies, setting up some config, and creating an AuthController to issue tokens, we can lock down our API endpoints to authenticated users only. We saw how to apply Guardian plugs to our router pipelines to enforce authentication, and access the current user within our controllers.

I hope this guide has given you a solid foundation for authenticating APIs in your own Elixir applications. Remember to follow security best practices, and don‘t be afraid to explore more advanced auth features as your app grows!

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *