HIGH broken access controlphoenixbearer tokens

Broken Access Control in Phoenix with Bearer Tokens

Broken Access Control in Phoenix with Bearer Tokens — how this specific combination creates or exposes the vulnerability

Broken Access Control occurs when authorization checks are missing, incomplete, or bypassed, allowing a user to access or modify resources they should not. In the Phoenix web framework, this commonly maps to the OWASP API Top 10 category and can be triggered when APIs rely solely on Bearer Tokens for authentication but do not enforce proper authorization per request. A token may identify a user or a scope, yet if routes or controllers do not validate that the authenticated subject is permitted to perform the action on the specific resource, the API is vulnerable.

Consider a typical Phoenix pipeline that authenticates with a Bearer Token but skips authorization checks for certain endpoints:

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  pipeline :api do
    plug :accepts, ["json"]
    plug MyAppWeb.Plugs.AuthenticateApiToken  # validates Bearer Token, assigns current_user
  end

  scope "/api", MyAppWeb do
    pipe_through :api

    get "/documents/:id", DocumentController, :show  # ← missing authorization check
    patch "/documents/:id", DocumentController, :update
  end
end

In this setup, the AuthenticateApiToken plug validates the Bearer Token and assigns a current_user in the connection assigns. However, if DocumentController does not verify that current_user has permission to view or modify the given :id, an authenticated user can tamper with the :id parameter and access or edit other users’ documents. This is a classic BOLA/IDOR pattern enabled by weak authorization around Bearer Token authentication.

Broken Access Control with Bearer Tokens can also arise when token scopes or roles are issued but not validated on each request. For example, an API might issue a token with a read:documents scope but the Phoenix endpoint performs no scope checking:

defmodule MyAppWeb.Plugs.AuthorizeDocumentScope do
  import Plug.Conn

  def init(default), do: default

  def call(conn, _opts) do
    case conn.assigns[:current_user] do
      %{scopes: scopes} when "read:documents" in scopes -> conn
      _ -> Plug.Conn.send_resp(conn, 403, "{\"error\": \"insufficient_scope\"}") |> halt()
    end
  end
end

defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  pipeline :api do
    plug :accepts, ["json"]
    plug MyAppWeb.Plugs.AuthenticateApiToken
    plug MyAppWeb.Plugs.AuthorizeDocumentScope   # ensures token has required scope
  end

  scope "/api", MyAppWeb do
    pipe_through :api

    get "/documents/:id", DocumentController, :show
  end
end

Even with scope validation, you must also enforce per-instance authorization (e.g., the document belongs to the user or the user’s team). Relying only on token-level attributes without checking the resource owner enables horizontal privilege escalation across records. This is why middleware authentication (Bearer Tokens) must be paired with explicit, request-level authorization checks in Phoenix controllers or via domain policies, mapping directly to the BOLA/IDOR checks performed by middleBrick’s scans.

Bearer Tokens-Specific Remediation in Phoenix — concrete code fixes

Remediation focuses on ensuring that every authenticated request is also authorized for the specific resource and required scope. Below are concrete, idiomatic Phoenix patterns that address Broken Access Control when using Bearer Tokens.

1. Add per-resource ownership check in the controller

After authenticating the token and assigning current_user, verify that the requested resource belongs to the user or an authorized role/team before proceeding:

defmodule MyAppWeb.DocumentController do
  use MyAppWeb, :controller

  plug :authorize_document when action in [:show, :update]

  def show(conn, %{"id" => id}) do
    document = MyApp.get_document!(id)
    # Ensure the document belongs to the authenticated user or their org
    if MyApp.authorized?(document, conn.assigns.current_user) do
      render(conn, "show.json", document: document)
    else
      send_resp(conn, 403, "{\"error\": \"forbidden\"}")
    end
  end

  defp authorize_document(conn, _opts) do
    with %{"id" => id} <- conn.params,
         document <- MyApp.get_document(id),
         user when not is_nil(user) <- conn.assigns[:current_user],
         true <- MyApp.authorized?(document, user) do
      attach_document(conn, document)
    else
      _ -> send_resp(conn, 403, "{\"error\": \"forbidden\"}") |> halt()
    end
  end
end

2. Validate token scopes and enforce them via a plug

Define a plug that inspects the token’s scopes (embedded in the token claims or fetched from a cache) and rejects requests missing required permissions:

defmodule MyAppWeb.Plugs.RequireScope do
  import Plug.Conn

  def init(required_scope), do: required_scope

  def call(conn, required_scope) do
    user = conn.assigns[:current_user]
    if Enum.member?(Map.get(user, :scopes, []), required_scope) do
      conn
    else
      send_resp(conn, 403, "{\"error\": \"insufficient_scope\"}") |> halt()
    end
  end
end

# Usage in router
pipeline :api do
  plug :accepts, ["json"]
  plug MyAppWeb.Plugs.AuthenticateApiToken
  plug MyAppWeb.Plugs.RequireScope, "write:documents"
end

3. Use domain policies for centralized authorization logic

Keep authorization decisions in a dedicated module so controllers remain thin and checks are consistent across endpoints:

defmodule MyApp.Policies.Document do
  def can_view?(%Document{user_id: owner_id}, %User{id: user_id}), do: owner_id == user_id
  def can_update?(%Document{owner_id: owner_id}, %User{id: user_id}), do: owner_id == user_id
  # Add team-based or role-based rules as needed
end

# In controller or plug
if MyApp.Policies.Document.can_view?(document, conn.assigns.current_user) do
  # proceed
end

By combining Bearer Token authentication in Phoenix with explicit, per-request authorization checks—covering ownership, scopes, and domain policies—you mitigate the risk of Broken Access Control and reduce the findings that tools like middleBrick would report under BOLA/IDOR and related checks.

Frequently Asked Questions

Why does authenticated access with Bearer Tokens still lead to authorization issues?
Authentication confirms identity; authorization decides what an identity can do. If Phoenix endpoints skip per-resource or per-scope checks, valid Bearer Tokens can still allow unauthorized access, creating Broken Access Control (BOLA/IDOR).
How can middleBrick findings help with Bearer Token authorization in Phoenix?
middleBrick scans test unauthenticated attack surfaces and can detect missing authorization checks around Bearer Token authentication, mapping findings to OWASP API Top 10 and providing remediation guidance to tighten per-request authorization in Phoenix.