Skip to main content

mix.exs

defmodule AttestoPhoenix.MixProject do
  @moduledoc false
  use Mix.Project

  alias AttestoPhoenix.Controller.DiscoveryController
  alias AttestoPhoenix.Controller.JWKSController
  alias AttestoPhoenix.Controller.PARController
  alias AttestoPhoenix.Controller.RegistrationController
  alias AttestoPhoenix.Controller.RevocationController
  alias AttestoPhoenix.Controller.TokenController
  alias AttestoPhoenix.Controller.UserinfoController
  alias AttestoPhoenix.OpenAPI.TokenEndpoint
  alias AttestoPhoenix.Schema.Authorization
  alias AttestoPhoenix.Schema.DPoPNonce
  alias AttestoPhoenix.Schema.DPoPReplay
  alias AttestoPhoenix.Schema.RefreshToken
  alias AttestoPhoenix.Store.EctoCodeStore
  alias AttestoPhoenix.Store.EctoNonceStore
  alias AttestoPhoenix.Store.EctoRefreshStore
  alias AttestoPhoenix.Store.EctoReplayCheck
  alias AttestoPhoenix.Store.PAR.ETS
  alias AttestoPhoenix.Store.Sweeper

  @version "0.19.1"
  @url "https://github.com/XukuLLC/attesto_phoenix"
  @maintainers ["Neil Berkman"]

  def project do
    [
      name: "AttestoPhoenix",
      app: :attesto_phoenix,
      version: @version,
      elixir: "~> 1.18",
      package: package(),
      source_url: @url,
      homepage_url: @url,
      maintainers: @maintainers,
      description:
        "Phoenix/Ecto OAuth 2.0 / OIDC authorization server layer over attesto: " <>
          "authorization, token, PAR, revocation, discovery, JWKS, UserInfo, " <>
          "protected-resource plugs, and Ecto-backed token stores.",
      deps: deps(),
      elixirc_paths: elixirc_paths(Mix.env()),
      docs: docs(),
      aliases: aliases(),
      dialyzer: [
        ignore_warnings: ".dialyzer_ignore.exs",
        plt_add_apps: [:mix, :ex_unit],
        plt_core_path: "priv/plts",
        plt_file: {:no_warn, "priv/plts/attesto_phoenix.plt"}
      ]
    ]
  end

  def cli do
    [preferred_envs: [precommit: :test]]
  end

  def application do
    [extra_applications: [:logger]]
  end

  defp elixirc_paths(:test), do: ["lib", "test/support"]
  defp elixirc_paths(_), do: ["lib"]

  # Co-develop against the sibling attesto checkout, but ONLY when explicitly
  # opted in via ATTESTO_PATH=1 (and the checkout exists). This is deliberately
  # NOT keyed on Mix.env alone: `mix hex.build` / `mix hex.publish` run in :dev,
  # and a path dep cannot be packaged ("only Hex packages can be dependencies"),
  # so a Mix.env-based switch would break publishing from a dev checkout. The
  # default - including every publish - resolves the published version
  # constraint; local development sets ATTESTO_PATH=1 to use the sibling. This
  # branch requires attesto 0.13 for OpenID Connect logout (RP-Initiated +
  # Back-Channel): Attesto.LogoutToken / EndSession / LogoutSessionStore and the
  # IDToken `sid` claim.
  defp attesto_dep do
    if System.get_env("ATTESTO_PATH") in ~w(1 true) and File.dir?("../attesto") do
      {:attesto, path: "../attesto"}
    else
      {:attesto, "~> 0.13"}
    end
  end

  defp deps do
    [
      # Core OAuth2/OIDC primitives: Token, IDToken, DPoP, MTLS, Scope,
      # AuthorizationCode, AuthorizationRequest, RefreshToken, Discovery,
      # OpenIDDiscovery, the store behaviours, and the base plugs.
      attesto_dep(),
      # Ecto-backed CodeStore/RefreshStore/NonceStore/ReplayCheck + the migration
      # generator.
      {:ecto_sql, "~> 3.10"},
      # Controllers + the attesto_routes/1 router macro.
      {:phoenix, "~> 1.7"},
      # Optional OpenAPI structs for hosts that publish an OpenApiSpex spec.
      {:open_api_spex, "~> 3.0", optional: true},
      # Plug behaviours for the protected-resource plugs (also a Phoenix
      # transitive dependency).
      {:plug, "~> 1.15"},
      # `mix attesto_phoenix.install` (Mix.Tasks.AttestoPhoenix.Install) is an
      # upgrade-aware Igniter installer: it inspects the host's config and router
      # and applies idempotent patches. Declared `optional: true` so the runtime
      # hex package never forces igniter onto a consumer that only depends on the
      # library at runtime. The dependency is present for the install task to
      # compile against and for a host that opts into running the installer, but
      # it is not a transitive runtime requirement. The `~> 0.5` requirement
      # admits the current 0.6 line.
      {:igniter, "~> 0.5", optional: true},

      # HTTP client for the default Client ID Metadata Document fetcher
      # (`AttestoPhoenix.ClientIdMetadata.Fetcher.Req`): Req -> Finch -> Mint,
      # which gives redirect control, connect/receive timeouts, and the
      # connect-by-IP pinning the SSRF guard needs (Mint's `:hostname` connect
      # option keeps TLS SNI + certificate verification on the original host
      # while the socket targets a validated IP). Declared `optional: true` so a
      # host that never enables CIMD - or that supplies its own `:fetcher` (e.g.
      # a CIMD proxy service) - pays nothing for it.
      {:req, "~> 0.5", optional: true},

      # test - the bundled Ecto stores run against a real Postgres repo.
      {:postgrex, ">= 0.0.0", only: [:dev, :test]},
      # test-only client interop: prove the protected-resource plug accepts
      # DPoP requests generated by an external Req client package.
      {:req_dpop, "~> 0.5", only: :test, runtime: false},
      # test-only HTTP origin server for the CIMD fetcher happy-path and
      # protocol-rejection tests (`...ClientIdMetadata.FetcherTest`).
      {:bypass, "~> 2.1", only: :test},

      # dev / quality
      {:ex_doc, "~> 0.40", only: :dev, runtime: false},
      {:credo, "~> 1.7", only: [:dev, :test], runtime: false},
      {:dialyxir, "~> 1.4", only: [:dev], runtime: false},
      {:mix_test_watch, "~> 1.4", only: :dev, runtime: false},
      {:quokka, "~> 2.12", only: [:dev, :test], runtime: false}
    ]
  end

  defp aliases do
    [
      setup: ["deps.get"],
      precommit: [
        "format --check-formatted",
        "compile --warnings-as-errors",
        "credo --strict",
        "test"
      ]
    ]
  end

  defp docs do
    [
      main: "readme",
      source_ref: "v#{@version}",
      source_url: @url,
      extras: [
        "README.md",
        "guides/examples.md",
        "guides/consumer_migration.md",
        "guides/proxy_canonical_host.md",
        "guides/replay_nonce_production.md",
        "guides/error_envelope.md",
        "guides/identity_assertion_grant.md",
        "notebooks/attesto_phoenix_demo.livemd",
        "CHANGELOG.md",
        "LICENSE"
      ],
      groups_for_extras: [
        Guides: ~r/guides\/.*/,
        Notebooks: ~r/notebooks\/.*/,
        Changelog: ~r/CHANGELOG\.md/,
        License: ~r/LICENSE/
      ],
      groups_for_modules: [
        Setup: [AttestoPhoenix, AttestoPhoenix.Config, AttestoPhoenix.Router],
        "Host contracts (behaviours)": [
          AttestoPhoenix.ClientStore,
          AttestoPhoenix.PrincipalStore,
          AttestoPhoenix.ScopePolicy,
          AttestoPhoenix.ConsentPolicy,
          AttestoPhoenix.RegistrationStore,
          AttestoPhoenix.EventSink
        ],
        Controllers: [
          TokenController,
          RevocationController,
          DiscoveryController,
          JWKSController,
          PARController,
          RegistrationController,
          UserinfoController
        ],
        OpenAPI: [
          TokenEndpoint
        ],
        Stores: [
          EctoCodeStore,
          EctoRefreshStore,
          EctoReplayCheck,
          EctoNonceStore,
          ETS,
          Sweeper
        ],
        Schemas: [
          Authorization,
          RefreshToken,
          DPoPReplay,
          DPoPNonce
        ],
        Shared: [
          AttestoPhoenix.OAuthError,
          AttestoPhoenix.Event,
          AttestoPhoenix.PARStore,
          AttestoPhoenix.RequestContext
        ]
      ]
    ]
  end

  defp package do
    [
      maintainers: @maintainers,
      licenses: ["MIT"],
      links: %{
        "Changelog" => "https://hexdocs.pm/attesto_phoenix/changelog.html",
        "GitHub" => @url
      },
      files: ~w(lib guides notebooks LICENSE mix.exs README.md CHANGELOG.md)
    ]
  end
end