AuthPlugIntroduct

A secure Elixir Plug for authenticating requests using RSA public key cryptography. Clients sign requests with their private key; AuthPlugIntroduct verifies the signature using the stored public key.

Installation

Add to your mix.exs deps:

def deps do
  [
    {:auth_plug_introduct, "~> 0.1.0"}
  ]
end

Configuration

In config/config.exs:

config :auth_plug_introduct,
  key_header: "key_id",        # Header with user UUID
  sign_header: "sign",         # Header with Base64-encoded signature
  repo: YourApp.Repo,
  user_schema: YourApp.User

User Schema

Your User schema must have:

schema "users" do
  field :uuid, :binary_id, primary_key: true
  field :pub_key, :string      # RSA public key in PEM format
end

Usage

Add to your Phoenix router:

pipeline :api_auth do
  plug AuthPlugIntroduct.AuthUser
end

scope "/api", YourAppWeb do
  pipe_through :api_auth

  get "/profile", ProfileController, :show
  post "/items", ItemController, :create
end

Access the authenticated user:

defmodule YourAppWeb.ProfileController do
  def show(conn, _params) do
    user = conn.assigns[:current_user]
    json(conn, %{uuid: user.uuid, email: user.email})
  end
end

How It Works

  1. Client includes headers: key_id (user UUID) and sign (Base64-encoded RSA signature)
  2. AuthPlugIntroduct extracts headers and loads user from database
  3. Verifies the signature against the request body using the user's public key
  4. On success: user assigned to conn.assigns[:current_user]
  5. On failure: returns 401 (missing/invalid headers) or 403 (signature invalid)

Key Generation

Generate RSA key pair:

openssl genrsa -out private_key.pem 2048
openssl rsa -in private_key.pem -pubout -out public_key.pem

Store the public key in your database:

public_key_pem = File.read!("public_key.pem")

YourApp.Repo.insert!(%YourApp.User{
  uuid: Ecto.UUID.generate(),
  email: "user@example.com",
  pub_key: public_key_pem,
  active: true
})

Error Responses

401 Unauthorized

403 Forbidden

Security Notes

Troubleshooting

401 errors: Verify headers match configuration names, check user exists in database

403 errors: Ensure request body matches exactly what was signed, verify key pair is valid

PEM format errors: Ensure public key starts with -----BEGIN PUBLIC KEY----- and has no extra whitespace