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"}
]
endConfiguration
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.UserUser 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
endUsage
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
endAccess 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
endHow It Works
-
Client includes headers:
key_id(user UUID) andsign(Base64-encoded RSA signature) - AuthPlugIntroduct extracts headers and loads user from database
- Verifies the signature against the request body using the user's public key
-
On success: user assigned to
conn.assigns[:current_user] - 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.pemStore 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
-
Missing
key_idorsignheader - User UUID not found in database
- Invalid Base64-encoded signature
403 Forbidden
- Signature verification failed
- Request body was modified after signing
- User's public key is invalid
Security Notes
- Always use HTTPS/TLS for all requests
- Currently uses SHA-1; consider upgrading to SHA-256 for production
- Never commit private keys to version control
- Implement rate limiting to prevent brute force attacks
-
Consider checking
user.activefield before granting access - Request body is read entirely into memory; set appropriate size limits
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