# SPDX-FileCopyrightText: 2026 ash_authentication_oauth2_server contributors # # SPDX-License-Identifier: MIT defmodule AshAuthentication.Phoenix.Oauth2Server.Router do @moduledoc """ Phoenix router macros for mounting the OAuth 2.1 authorization server. `use` this module inside your router to gain access to `oauth2_server_consent_routes/1` (browser-facing, user consent) and `oauth2_server_protocol_routes/1` (client-facing protocol endpoints). ## Example defmodule MyAppWeb.Router do use Phoenix.Router use AshAuthentication.Phoenix.Oauth2Server.Router scope "/" do pipe_through :browser oauth2_server_consent_routes oauth2_server: MyApp.Oauth2Server end scope "/" do pipe_through :api oauth2_server_protocol_routes oauth2_server: MyApp.Oauth2Server end end The two macros forward to: * `AshAuthentication.Phoenix.Oauth2Server.ConsentRouter` — handles `/oauth/authorize` (the user-driven consent step). * `AshAuthentication.Phoenix.Oauth2Server.ProtocolRouter` — handles `/oauth/register`, `/oauth/token`, `/oauth/revoke`, and the three metadata documents under `/.well-known`. """ defmacro __using__(_opts) do quote do import AshAuthentication.Phoenix.Oauth2Server.Router, only: [ oauth2_server_consent_routes: 0, oauth2_server_consent_routes: 1, oauth2_server_protocol_routes: 0, oauth2_server_protocol_routes: 1 ] end end @doc """ Generate the routes for the user-driven consent step of an OAuth 2.1 authorization-server flow. Mount this inside a scope that pipes through your **browser** pipeline (with `:protect_from_forgery` and session loading) — both the consent GET and POST need a logged-in user and CSRF protection. ## Example scope "/" do pipe_through :browser oauth2_server_consent_routes oauth2_server: MyApp.Oauth2Server end ## Options * `:oauth2_server` (required) — your `Oauth2Server` config module. * `:path` — base path. Defaults to `/oauth/authorize`. * `:consent_view` — module exposing `render(:consent, assigns)`. Defaults to `AshAuthentication.Phoenix.Oauth2Server.ConsentView`. """ defmacro oauth2_server_consent_routes(opts \\ []) when is_list(opts) do quote location: :keep do opts = unquote(opts) server = Keyword.fetch!(opts, :oauth2_server) path = Keyword.get(opts, :path, "/oauth/authorize") consent_view = Keyword.get(opts, :consent_view, AshAuthentication.Phoenix.Oauth2Server.ConsentView) scope "/", alias: false do forward path, AshAuthentication.Phoenix.Oauth2Server.ConsentRouter, oauth2_server: server, consent_view: consent_view end end end @doc """ Generate the routes for the client-facing OAuth 2.1 protocol endpoints — discovery, dynamic client registration, token, and revocation. Mount this inside a scope that pipes through your **API** pipeline. These endpoints are called by external OAuth clients without a browser session, so CSRF must NOT apply. ## Example scope "/" do pipe_through :api oauth2_server_protocol_routes oauth2_server: MyApp.Oauth2Server end ## Options * `:oauth2_server` (required) — your `Oauth2Server` config module. * `:oauth_path` — prefix for `/token`, `/register`, etc. Defaults to `/oauth`. * `:well_known_path` — prefix for `/oauth-authorization-server`, `/oauth-protected-resource`, `/openid-configuration`. Defaults to `/.well-known`. """ defmacro oauth2_server_protocol_routes(opts \\ []) when is_list(opts) do quote location: :keep do opts = unquote(opts) server = Keyword.fetch!(opts, :oauth2_server) oauth_path = Keyword.get(opts, :oauth_path, "/oauth") well_known_path = Keyword.get(opts, :well_known_path, "/.well-known") scope "/", alias: false do forward oauth_path, AshAuthentication.Phoenix.Oauth2Server.ProtocolRouter, oauth2_server: server forward well_known_path, AshAuthentication.Phoenix.Oauth2Server.ProtocolRouter, oauth2_server: server end end end end