Skip to main content

lib/skill_kit/authorization_provider.ex

defmodule SkillKit.AuthorizationProvider do
  @moduledoc """
  Behaviour contract for scope-resolution providers used by `SkillKit.Authorization.authorize/3`.

  An authorization provider is any module that implements this behaviour. The single
  callback `resolve_scopes/1` receives an opaque context map and returns the list of
  scope strings granted to the caller, or an error.

  ## Implementation Example

      defmodule MyApp.TokenProvider do
        @behaviour SkillKit.AuthorizationProvider

        @impl SkillKit.AuthorizationProvider
        def resolve_scopes(%{token: token}) do
          case MyApp.Tokens.verify(token) do
            {:ok, claims} -> {:ok, claims["scopes"]}
            {:error, reason} -> {:error, reason}
          end
        end
      end

  ## Errors

  Provider errors pass through unchanged to the caller of `authorize/3`.
  For example, `{:error, :token_expired}` returned here arrives as
  `{:error, :token_expired}` at the call site — providers must NOT be
  rescued or normalized inside `SkillKit.Authorization`.

  ## Context Shape

  The `context` argument is fully opaque (`map()`). No required keys are
  enforced by the behaviour contract. Each provider defines its own
  expected shape and pattern-matches accordingly.
  """

  @doc """
  Resolves the scope list for the given context.

  Returns `{:ok, scopes}` on success where `scopes` is a flat list of scope
  strings (e.g. `["admin:read", "tools:*"]`), or `{:error, reason}` on failure.

  The `context` map is opaque — implementations are free to require any
  structure and should return `{:error, reason}` for missing or invalid inputs.
  """
  @callback resolve_scopes(context :: map()) :: {:ok, [String.t()]} | {:error, term()}
end