diff --git a/README.md b/README.md index dce87bb..d5eec03 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,27 @@ oauth_token_path POST /oauth/revoke TokenController :revoke Please read the [ExOauth2Provider](https://github.com/danschultzer/ex_oauth2_provider) documentation for further customization. +### Token introspection + +Token introspection (as per [RFC-7662](https://datatracker.ietf.org/doc/html/rfc7662)]) can be enabled with `oauth_token_introspection_routes()`: + +```elixir +defmodule MyAppWeb.Router do + use MyAppWeb, :router + use PhoenixOauth2Provider.Router, otp_app: :my_app + + scope "/" do + pipe_through :api + + oauth_token_introspection_routes() + end +end +``` + +```text +oauth_token_path POST /oauth/introspect TokenController :introspect +``` + ## Configuration ### Templates diff --git a/lib/phoenix_oauth2_provider/controllers/token_controller.ex b/lib/phoenix_oauth2_provider/controllers/token_controller.ex index 6217e8f..4c1941b 100644 --- a/lib/phoenix_oauth2_provider/controllers/token_controller.ex +++ b/lib/phoenix_oauth2_provider/controllers/token_controller.ex @@ -33,4 +33,19 @@ defmodule PhoenixOauth2Provider.TokenController do |> json(error) end end + + def introspect(conn, params, config) do + params + |> Token.introspect(config) + |> case do + {:ok, response} -> + json(conn, response) + + {:error, error, status} -> + conn + |> put_status(status) + |> json(error) + end + end + end diff --git a/lib/phoenix_oauth2_provider/router.ex b/lib/phoenix_oauth2_provider/router.ex index eef4e6f..abe39cb 100644 --- a/lib/phoenix_oauth2_provider/router.ex +++ b/lib/phoenix_oauth2_provider/router.ex @@ -100,6 +100,14 @@ defmodule PhoenixOauth2Provider.Router do end end + defmacro oauth_token_introspection_routes(options \\ []) do + quote location: :keep do + oauth_scope unquote(options), @phoenix_oauth2_provider_config do + post "/introspect", TokenController, :introspect + end + end + end + @doc false defmacro oauth_scope(options \\ [], config \\ [], do: context) do quote do diff --git a/test/phoenix_oauth2_provider/controllers/token_controller_test.exs b/test/phoenix_oauth2_provider/controllers/token_controller_test.exs index 3e2ac69..090da78 100644 --- a/test/phoenix_oauth2_provider/controllers/token_controller_test.exs +++ b/test/phoenix_oauth2_provider/controllers/token_controller_test.exs @@ -120,4 +120,36 @@ defmodule PhoenixOauth2Provider.TokenControllerTest do |> List.last() |> Map.get(:token) end + + describe "with introspection strategy" do + setup %{conn: conn, application: application} do + user = Fixtures.user() + access_token = Fixtures.access_token(%{application: application, user: user, use_refresh_token: true}) + request = %{client_id: application.uid, + client_secret: application.secret, + token: access_token.token} + + {:ok, conn: conn, request: request, access_token: access_token} + end + + test "introspect/2 with access token", %{conn: conn, request: request, access_token: access_token} do + conn = post conn, Routes.oauth_token_path(conn, :introspect, request) + body = json_response(conn, 200) + assert %{"active" => true, "scope" => actual_scopes} = body + assert actual_scopes == access_token.scopes + end + + test "introspect/2 with refresh token", %{conn: conn, request: request, access_token: access_token} do + IO.inspect(conn) + conn = post conn, Routes.oauth_token_path(conn, :introspect, Map.merge(request, %{token: access_token.refresh_token})) + body = json_response(conn, 200) + assert %{"active" => true} = body + end + + test "introspect/2 with invalid token", %{conn: conn, request: request} do + conn = post conn, Routes.oauth_token_path(conn, :introspect, Map.merge(request, %{token: "invalid"})) + body = json_response(conn, 200) + assert body == %{"active" => false} + end + end end diff --git a/test/support/lib/dummy_web/router.ex b/test/support/lib/dummy_web/router.ex index 7706f21..09c0375 100644 --- a/test/support/lib/dummy_web/router.ex +++ b/test/support/lib/dummy_web/router.ex @@ -18,5 +18,6 @@ defmodule DummyWeb.Router do pipe_through :browser oauth_routes() + oauth_token_introspection_routes() end end