Back to home

Pike

A lightweight Elixir library for API key authentication and fine-grained authorization.

Key Features

  • API key management
  • Policy-based authorization for granular control.
  • Pluggable architecture for easy integration.
  • Minimal dependencies and high performance.

README

Pike

Hex version badgeLicense badge

Pike enables API key enforcement with zero external dependencies, pluggable storage backends, resource-aware permissions, and a flexible DSL for defining authorization at the controller and action level.


Chapters


Usage

Installation

Add Pike to your dependencies:

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

Configuration

Global application-wide config (only needed if overriding defaults):

config :pike,
  store: YourApp.APIStore,
  on_auth_failure: {Pike.Responder.Default, :auth_failed}

Create your ETS Table:

defmodule YourApp.APIStore do
  use Pike.Store.ETS, table_name: :default_api_keys
end

Using Plug

At its simplest:

plug Pike.AuthorizationPlug

๐Ÿ”€ Multiple Pipelines

You can defineindependent pipelinesfor different API key types:

pipeline :public_api do
  plug Pike.AuthorizationPlug,
    store: YourApp.APIStore,
    assign_to: :public_api_key
end

pipeline :partner_api do
  plug Pike.AuthorizationPlug,
    store: YourApp.AlternativeAPIStore,
    on_auth_failure: {MyApp.Responder, :auth_failed},
    assign_to: :partner_key
end

Then route accordingly:

scope "/v1/public", MyAppWeb do
  pipe_through [:api, :public_api]
end

scope "/v1/partner", MyAppWeb do
  pipe_through [:api, :partner_api]
end

Controller Integration

Declare expected permissions using a DSL:

defmodule MyAppWeb.ProductController do
  use MyAppWeb, :controller
  use Pike.Authorization, resource: "Products"

  # Uses controller-level resource ("Products")
  @require_permission action: :read
  def index(conn, _params), do: # ...

  # Resource becomes "ProductsMeta"
  @require_permission action: :read, append: "Meta"
  def meta(conn, _params), do: # ...

  # Completely overrides: resource = "VariableProducts"
  @require_permission action: :read, override: "VariableProducts"
  def variations(conn, _params), do: # ...
end

Permission Model

Each API key must define a list of permissions:

%{
  key: "abc123",
  permissions: [
    %{resource: "Products", scopes: [:read, :write]},
    %{resource: "ProductsMeta", scopes: [:read]},
    %{resource: "VariableProducts", scopes: [:read]}
  ]
}

Key Assignment

After successful authentication, the API key is available via:

conn.assigns[:pike_api_key]  # or :partner_api_key if overridden

This lets you:

  • Inspect the key
  • Track usage
  • Enforce tenant or user-level scoping

Key Management

Pike provides functions for managing API keys:

# Create a new key
Pike.insert(%{
  key: "abc123",
  enabled: true,  # Optional, defaults to true
  permissions: [
    %{resource: "Products", scopes: [:read, :write]}
  ]
})

# Enable/disable a key
Pike.disable_key("abc123")
Pike.enable_key("abc123")

# Check if a key is enabled
Pike.key_enabled?("abc123")

# Delete a key
Pike.delete_key("abc123")

# Update a key
Pike.update_key("abc123", %{
  permissions: [
    %{resource: "Products", scopes: [:read]}
  ]
})

Disabled keys will return a:disablederror reason, which is handled by the responder.


Store Backend

Pike uses a pluggable store interface:

@callback get_key(String.t()) :: {:ok, map()} | :error | {:error, :disabled}
@callback action?(map(), resource :: String.t(), action :: atom()) :: boolean()
@callback insert(map()) :: :ok | {:error, term()}  # optional
@callback delete_key(String.t()) :: :ok | {:error, term()}  # optional
@callback update_key(String.t(), map()) :: :ok | {:error, term()}  # optional

You can provide your own module and configure it per plug or globally.


๐Ÿ”– License

MIT

License

Pike is released under theMIT License.