-
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add require authorization plug (#21)
* Add require authorization plug * Remove duplicate codes from RequireAuthorization plug * Update README * Test added for require authorization plug --------- Co-authored-by: Ali Shirvani <[email protected]>
- Loading branch information
Showing
4 changed files
with
128 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
defmodule Oidcc.Plug.RequireAuthorization do | ||
@moduledoc """ | ||
Ensure authorization token provided. | ||
This module should be used together with `Oidcc.Plug.ExtractAuthorization`. | ||
```elixir | ||
defmodule SampleAppWeb.Endpoint do | ||
use Phoenix.Endpoint, otp_app: :sample_app | ||
# ... | ||
plug Oidcc.Plug.ExtractAuthorization | ||
plug Oidcc.Plug.RequireAuthorization | ||
# Check Token with `Oidcc.Plug.IntrospectToken`, `Oidcc.Plug.LoadUserinfo` or `Oidcc.Plug.ValidateJwtToken` | ||
plug SampleAppWeb.Router | ||
end | ||
``` | ||
""" | ||
@moduledoc since: "0.1.0" | ||
|
||
@behaviour Plug | ||
|
||
import Plug.Conn, only: [halt: 1, send_resp: 3, put_resp_header: 3] | ||
|
||
alias Oidcc.Plug.ExtractAuthorization | ||
|
||
@typedoc """ | ||
Plug Configuration Options | ||
## Options | ||
* `send_missing_token_response` - Customize Error Response for missing token | ||
""" | ||
@typedoc since: "0.1.0" | ||
@type opts :: [ | ||
send_missing_token_response: (conn :: Plug.Conn.t() -> Plug.Conn.t()) | ||
] | ||
|
||
@impl Plug | ||
def init(opts), | ||
do: | ||
Keyword.validate!(opts, | ||
send_missing_token_response: &__MODULE__.send_missing_token_response/1 | ||
) | ||
|
||
@impl Plug | ||
def call(%Plug.Conn{private: %{ExtractAuthorization => nil}} = conn, opts) do | ||
send_missing_token_response = Keyword.fetch!(opts, :send_missing_token_response) | ||
|
||
send_missing_token_response.(conn) | ||
end | ||
|
||
def call(%Plug.Conn{private: %{ExtractAuthorization => _access_token}} = conn, _opts), do: conn | ||
|
||
def call(%Plug.Conn{} = _conn, _opts) do | ||
raise """ | ||
The plug Oidcc.Plug.ExtractAuthorization must be run before this plug | ||
""" | ||
end | ||
|
||
@doc false | ||
@spec send_missing_token_response(conn :: Plug.Conn.t()) :: Plug.Conn.t() | ||
def send_missing_token_response(conn) do | ||
conn | ||
|> halt() | ||
|> put_resp_header("www-authenticate", "Bearer") | ||
|> send_resp(:unauthorized, "The authorization token is required") | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
defmodule Oidcc.Plug.RequireAuthorizationTest do | ||
use ExUnit.Case, async: false | ||
use Plug.Test | ||
|
||
alias Oidcc.Plug.ExtractAuthorization | ||
alias Oidcc.Plug.RequireAuthorization | ||
|
||
doctest RequireAuthorization | ||
|
||
describe inspect(&RequireAuthorization.call/2) do | ||
test "errors without ExtractAuthorization" do | ||
opts = | ||
RequireAuthorization.init([]) | ||
|
||
assert_raise RuntimeError, fn -> | ||
"get" | ||
|> conn("/", "") | ||
|> RequireAuthorization.call(opts) | ||
end | ||
end | ||
|
||
test "send error response if no token provided" do | ||
opts = | ||
RequireAuthorization.init([]) | ||
|
||
assert %{ | ||
halted: true, | ||
status: 401, | ||
resp_headers: [ | ||
{"cache-control", "max-age=0, private, must-revalidate"}, | ||
{"www-authenticate", "Bearer"} | ||
] | ||
} = | ||
"get" | ||
|> conn("/", "") | ||
|> put_private(ExtractAuthorization, nil) | ||
|> RequireAuthorization.call(opts) | ||
end | ||
|
||
test "pass if token provided" do | ||
opts = | ||
RequireAuthorization.init([]) | ||
|
||
assert %{halted: false} = | ||
"get" | ||
|> conn("/", "") | ||
|> put_private(ExtractAuthorization, "some_access_token") | ||
|> RequireAuthorization.call(opts) | ||
end | ||
end | ||
end |