Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds computed configurations #161

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,29 @@
client_secret: System.get_env("AUTH0_CLIENT_SECRET")
```

**or** with computed configurations:

```elixir
defmodule MyApp.ConfigFrom do
def get_domain(%Plug.Conn{} = conn) do
...
end

def get_client_id(%Plug.Conn{} = conn) do
...
end

def get_client_secret(%Plug.Conn{} = conn) do
...
end
end
```

```elixir
config :ueberauth, Ueberauth.Strategy.Auth0.OAuth,
config_from: MyApp.ConfigFrom
```

See the `Ueberauth.Strategy.Auth0` module docs for more
configuration options.

Expand Down
8 changes: 4 additions & 4 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config
# and its dependencies with the aid of the Config module.
import Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
Expand All @@ -27,5 +27,5 @@ use Mix.Config
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
if Mix.env() == :test, do: import_config("test.exs")
# import_config "#{config_env()}.exs"
if config_env() == :test, do: import_config("test.exs")
6 changes: 3 additions & 3 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use Mix.Config
import Config

config :ueberauth, Ueberauth,
json_library: Jason,
Expand All @@ -8,8 +8,8 @@ config :ueberauth, Ueberauth,

config :ueberauth, Ueberauth.Strategy.Auth0.OAuth,
domain: "example-app.auth0.com",
client_id: "clientidsomethingrandom",
client_secret: "clientsecret-somethingsecret"
client_id: "example-client-id",
client_secret: "example-client-secret"

config :exvcr,
vcr_cassette_library_dir: "test/fixtures/vcr_cassettes"
Expand Down
7 changes: 5 additions & 2 deletions lib/ueberauth/strategy/auth0.ex
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ defmodule Ueberauth.Strategy.Auth0 do

module = option(conn, :oauth2_module)

callback_url = module.authorize_url!(opts, [otp_app: option(conn, :otp_app)])
callback_url = module.authorize_url!(conn, opts, otp_app: option(conn, :otp_app))

redirect!(conn, callback_url)
end
Expand All @@ -132,7 +132,10 @@ defmodule Ueberauth.Strategy.Auth0 do
module = option(conn, :oauth2_module)
redirect_uri = callback_url(conn)

result = module.get_token!([code: code, redirect_uri: redirect_uri], [otp_app: option(conn, :otp_app)])
result =
module.get_token!(conn, [code: code, redirect_uri: redirect_uri],
otp_app: option(conn, :otp_app)
)

case result do
{:ok, client} ->
Expand Down
77 changes: 65 additions & 12 deletions lib/ueberauth/strategy/auth0/oauth.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,25 @@ defmodule Ueberauth.Strategy.Auth0.OAuth do
client_id: {:system, "AUTH0_CLIENT_ID"},
client_secret: {:system, "AUTH0_CLIENT_SECRET"}

Or using a computed configuration:

defmodule MyApp.ConfigFrom do
def get_domain(%Plug.Conn{} = conn) do
...
end

def get_client_id(%Plug.Conn{} = conn) do
...
end

def get_client_secret(%Plug.Conn{} = conn) do
...
end
end

config :ueberauth, Ueberauth.Strategy.Auth0.OAuth,
config_from: MyApp.ConfigFrom

The JSON serializer used is the same as `Ueberauth` so if you need to
customize it, you can configure it in the `Ueberauth` configuration:

Expand All @@ -24,8 +43,9 @@ defmodule Ueberauth.Strategy.Auth0.OAuth do
alias OAuth2.Client
alias OAuth2.Strategy.AuthCode

def options(otp_app) do
def options(conn, otp_app) do
configs = Application.get_env(otp_app || :ueberauth, Ueberauth.Strategy.Auth0.OAuth)
configs = compute_configs(conn, configs)

unless configs do
raise(
Expand Down Expand Up @@ -63,29 +83,27 @@ defmodule Ueberauth.Strategy.Auth0.OAuth do
This will be setup automatically for you in `Ueberauth.Strategy.Auth0`.
These options are only useful for usage outside the normal callback phase of Ueberauth.
"""
def client(opts \\ []) do
opts
|> Keyword.get(:otp_app)
|> options()
def client(conn, opts \\ []) do
otp_app = Keyword.get(opts, :otp_app)

options(conn, otp_app)
|> Keyword.merge(opts)
|> Client.new()
end

@doc """
Provides the authorize url for the request phase of Ueberauth. No need to call this usually.
"""
def authorize_url!(params \\ [], opts \\ []) do
opts
|> client
def authorize_url!(conn, params \\ [], opts \\ []) do
client(conn, opts)
|> Client.authorize_url!(params)
end

def get_token!(params \\ [], opts \\ []) do
def get_token!(conn, params \\ [], opts \\ []) do
otp_app = Keyword.get(opts, :otp_app)

client_secret =
otp_app
|> options()
options(conn, otp_app)
|> Keyword.get(:client_secret)

params = Keyword.merge(params, client_secret: client_secret)
Expand All @@ -97,7 +115,7 @@ defmodule Ueberauth.Strategy.Auth0.OAuth do
|> Keyword.get(:client_options, [])
|> Keyword.merge(otp_app: otp_app)

Client.get_token(client(client_options), params, headers, opts)
Client.get_token(client(conn, client_options), params, headers, opts)
end

# Strategy Callbacks
Expand All @@ -114,4 +132,39 @@ defmodule Ueberauth.Strategy.Auth0.OAuth do

defp get_config_value({:system, value}), do: System.get_env(value)
defp get_config_value(value), do: value

defp compute_configs(conn, configs) do
case conn do
%Plug.Conn{} = conn when not is_nil(configs) ->
with module when not is_nil(module) <-
Keyword.get(configs, :config_from),
{:loaded, {:module, _module}} <- {:loaded, Code.ensure_loaded(module)},
{:exported, true} <- {:exported, function_exported?(module, :get_domain, 1)},
{:exported, true} <- {:exported, function_exported?(module, :get_client_id, 1)},
{:exported, true} <- {:exported, function_exported?(module, :get_client_secret, 1)} do
configs
|> Keyword.merge(
domain: apply(module, :get_domain, [conn]),
client_id: apply(module, :get_client_id, [conn]),
client_secret: apply(module, :get_client_secret, [conn])
)
else
silvagustin marked this conversation as resolved.
Show resolved Hide resolved
{:loaded, {:error, :nofile}} ->
raise("Couldn't load module from `:config_from`")

{:exported, false} ->
raise(
"When using `:config_from`, the given module should export 3 functions: `get_domain/1`, `get_client_id/1` and `get_client_secret/1`"
)

# Used base configuration.
_ ->
configs
end

# Both configurations weren't found.
_ ->
configs
end
end
end
10 changes: 7 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ defmodule UeberauthAuth0.Mixfile do
# Type checking
dialyzer: [
plt_core_path: "_build/#{Mix.env()}"
]
],
elixirc_paths: elixirc_paths(Mix.env())
]
end

Expand All @@ -51,8 +52,8 @@ defmodule UeberauthAuth0.Mixfile do
{:ex_doc, ">= 0.0.0", only: :dev, runtime: false},

# Testing:
{:exvcr, "~> 0.10", only: :test},
{:excoveralls, "~> 0.11", only: :test},
{:exvcr, "~> 0.10", only: [:test]},
{:excoveralls, "~> 0.11", only: [:test]},

# Type checking
{:dialyxir, "~> 1.2.0", only: [:dev, :test], runtime: false},
Expand Down Expand Up @@ -89,4 +90,7 @@ defmodule UeberauthAuth0.Mixfile do
}
]
end

defp elixirc_paths(:test), do: ["lib", "test/support", "test/configs"]
defp elixirc_paths(_), do: ["lib"]
end
3 changes: 3 additions & 0 deletions test/configs/bad_config_from.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :ueberauth, Ueberauth.Strategy.Auth0.OAuth, config_from: Ueberauth.Support.BadConfigFrom
3 changes: 3 additions & 0 deletions test/configs/config_from.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import Config

config :ueberauth, Ueberauth.Strategy.Auth0.OAuth, config_from: Ueberauth.Support.ConfigFrom
69 changes: 59 additions & 10 deletions test/strategy/auth0/oauth_test.exs
Original file line number Diff line number Diff line change
@@ -1,27 +1,76 @@
defmodule Ueberauth.Strategy.Auth0.OAuthTest do
use ExUnit.Case

import Ueberauth.Strategy.Auth0.OAuth, only: [client: 0, client: 1]
import Ueberauth.Strategy.Auth0.OAuth, only: [client: 1, client: 2]

@test_domain "example-app.auth0.com"
@conn %Plug.Conn{}

setup do
{:ok, %{client: client()}}
describe "when default configurations are used" do
setup do
{:ok, %{client: client(@conn)}}
end

test "creates correct client", %{client: client} do
asserts_client_creation(client)
end

test "raises when there is no configuration" do
assert_raise(RuntimeError, ~r/^Expected to find settings under.*/, fn ->
client(@conn, otp_app: :unknown_auth0_otp_app)
end)
end
end

describe "when right custom/computed configurations are used" do
setup do
load_configs("config_from")
{:ok, %{client: client(@conn, conn: @conn)}}
end

test "creates correct client", %{client: client} do
asserts_client_creation(client)
end
end

test "creates correct client", %{client: client} do
assert client.client_id == "clientidsomethingrandom"
assert client.client_secret == "clientsecret-somethingsecret"
describe "when bad custom/computed configurations are used" do
setup do
load_configs("bad_config_from")
end

test "raises when there is bad ConfigFrom module" do
assert_raise(
RuntimeError,
~r/^When using `:config_from`, the given module should export 3 functions:*/,
fn ->
client(@conn, conn: @conn)
end
)
end
end

defp asserts_client_creation(client) do
assert client.client_id == "example-client-id"
assert client.client_secret == "example-client-secret"
assert client.redirect_uri == ""
assert client.strategy == Ueberauth.Strategy.Auth0.OAuth
assert client.authorize_url == "https://#{@test_domain}/authorize"
assert client.token_url == "https://#{@test_domain}/oauth/token"
assert client.site == "https://#{@test_domain}"
end

test "raises when there is no configuration" do
assert_raise(RuntimeError, ~r/^Expected to find settings under.*/, fn ->
client(otp_app: :unknown_auth0_otp_app)
end)
defp load_configs(filename) do
"test/configs/#{filename}.exs"
|> Config.Reader.read!()
|> Application.put_all_env()

module =
:ueberauth
|> Application.get_env(Ueberauth.Strategy.Auth0.OAuth)
|> Keyword.get(:config_from)

{:module, _module} = Code.ensure_loaded(module)

:ok
end
end
Loading