docs/using-pike-authorization-plug.md

# Using Pike's AuthorizationPlug

This guide covers the various ways to use Pike's `AuthorizationPlug` to authenticate and authorize API requests using Bearer tokens.

## Basic Usage

The simplest way to use Pike's authorization system is to add the plug to your pipeline:

```elixir
# In a Phoenix router
pipeline :api do
  plug :accepts, ["json"]
  plug Pike.AuthorizationPlug
end

scope "/api", MyAppWeb do
  pipe_through :api
  
  resources "/products", ProductController
end
```

This configuration:
1. Extracts the Bearer token from the `Authorization` header
2. Validates the token against the configured store
3. Assigns the authenticated key to `conn.assigns[:pike_api_key]`
4. Rejects unauthorized requests with appropriate status codes

## Configuration Options

The `AuthorizationPlug` accepts several options to customize its behavior:

```elixir
plug Pike.AuthorizationPlug,
  store: MyApp.CustomStore,
  assign_to: :current_api_key,
  on_auth_failure: MyApp.CustomResponder
```

### Available Options

| Option | Description | Default |
|--------|-------------|---------|
| `store` | Module implementing the `Pike.Store` behavior | `Pike.Store.ETS` |
| `assign_to` | The assign key where the API key will be stored | `:pike_api_key` |
| `on_auth_failure` | Module implementing the `Pike.Responder` behavior | `Pike.Responder.Default` |

## Multiple Authorization Pipelines

Pike supports multiple independent authorization pipelines, each with its own configuration:

```elixir
# In a Phoenix router
pipeline :public_api do
  plug :accepts, ["json"]
  plug Pike.AuthorizationPlug, store: MyApp.PublicKeyStore
end

pipeline :admin_api do
  plug :accepts, ["json"]
  plug Pike.AuthorizationPlug, store: MyApp.AdminKeyStore
end

scope "/api/v1", MyAppWeb do
  pipe_through :public_api
  
  resources "/products", ProductV1.ProductController, only: [:index, :show]
end

scope "/api/admin", MyAppWeb do
  pipe_through :admin_api
  
  resources "/products", Admin.ProductController
end
```

This setup allows different API endpoints to use different key stores and authorization rules.

## Using with Controllers

Pike works seamlessly with controllers via the authorization DSL:

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

  @require_permission action: :read
  def index(conn, _params) do
    # This action requires read permission on Products
    products = MyApp.Catalog.list_products()
    render(conn, "index.json", products: products)
  end
  
  @require_permission action: :read
  def show(conn, %{"id" => id}) do
    # This action requires read permission on Products
    product = MyApp.Catalog.get_product!(id)
    render(conn, "show.json", product: product)
  end
  
  @require_permission action: :write
  def create(conn, %{"product" => product_params}) do
    # This action requires write permission on Products
    # ...
  end
end
```

## Manual Authorization

For more complex scenarios, you can perform manual authorization checks:

```elixir
def custom_action(conn, _params) do
  api_key = conn.assigns[:pike_api_key]
  
  cond do
    Pike.action?(api_key, resource: "Products", action: :admin) ->
      # Handle admin action
      
    Pike.action?(api_key, resource: "Products", action: :read) ->
      # Handle read-only action
      
    true ->
      conn
      |> put_status(:forbidden)
      |> json(%{error: "Insufficient permissions"})
      |> halt()
  end
end
```

## Custom Extraction Logic

If you need custom token extraction logic, you can create your own plug that sets the API key and then use Pike for authorization only:

```elixir
defmodule MyApp.CustomAuthPlug do
  import Plug.Conn
  
  def init(opts), do: opts
  
  def call(conn, _opts) do
    # Custom logic to extract and validate token
    # ...
    
    # Assign the validated key to conn
    assign(conn, :pike_api_key, api_key)
  end
end

# In your pipeline
pipeline :api do
  plug :accepts, ["json"]
  plug MyApp.CustomAuthPlug
  # Note: Pike doesn't support skipping extraction directly.
  # Simply assign the key to conn.assigns[:pike_api_key] in your custom plug
  plug Pike.AuthorizationPlug
end
```

## Integration with Authentication Frameworks

Pike can be used alongside other authentication frameworks:

```elixir
pipeline :api do
  plug :accepts, ["json"]
  
  # First authenticate the user
  plug MyApp.Authentication
  
  # Then authorize API access
  plug Pike.AuthorizationPlug
end
```

## Customizing Failure Responses

Implement a custom responder to tailor error responses:

```elixir
defmodule MyApp.CustomResponder do
  @behaviour Pike.Responder
  
  import Plug.Conn
  
  @impl true
  def auth_failed(conn, :missing_key) do
    conn
    |> put_status(:unauthorized)
    |> Phoenix.Controller.json(%{
        error: "Authentication required",
        detail: "Please provide a valid API token"
    })
    |> halt()
  end
  
  @impl true
  def auth_failed(conn, :not_found) do
    # Custom handling for invalid tokens
    # ...
  end
  
  @impl true
  def auth_failed(conn, :unauthorized_action) do
    # Custom handling for valid tokens with insufficient permissions
    # ...
  end
end
```

## Testing with Pike

For testing controllers that use Pike:

```elixir
# In your test setup
setup do
  # Create a test API key with appropriate permissions
  api_key = %{
    key: "test_key_123",
    enabled: true,
    permissions: [
      %{resource: "Products", scopes: [:read, :write]}
    ]
  }
  
  # Insert it into the store
  Pike.insert(api_key)
  
  # Use it in your test connections
  conn = 
    build_conn()
    |> put_req_header("authorization", "Bearer test_key_123")
  
  {:ok, conn: conn}
end
```

By leveraging these various approaches, you can adapt Pike's authorization system to fit the specific needs of your application.