From fac96c7efa59c1b4cbd2363240abb606638196f6 Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:14:01 +0100 Subject: [PATCH 1/6] Add hd as param, update documentation for authorize_url! and on put_param filter out hd param when content is an empty string --- lib/oauth2/client.ex | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/oauth2/client.ex b/lib/oauth2/client.ex index c3bbf13..f96bb16 100644 --- a/lib/oauth2/client.ex +++ b/lib/oauth2/client.ex @@ -46,6 +46,7 @@ defmodule OAuth2.Client do @type token :: AccessToken.t | nil @type token_method :: :post | :get | atom @type token_url :: binary + @type hd :: binary @type t :: %Client{ authorize_url: authorize_url, @@ -60,7 +61,8 @@ defmodule OAuth2.Client do strategy: strategy, token: token, token_method: token_method, - token_url: token_url + token_url: token_url, + hd: hd } defstruct authorize_url: "/oauth/authorize", @@ -75,7 +77,8 @@ defmodule OAuth2.Client do strategy: OAuth2.Strategy.AuthCode, token: nil, token_method: :post, - token_url: "/oauth/token" + token_url: "/oauth/token", + hd: "" @doc """ Builds a new `OAuth2.Client` struct using the `opts` provided. @@ -101,6 +104,7 @@ defmodule OAuth2.Client do Defaults to `:post` * `token_url` - absolute or relative URL path to the token endpoint. Defaults to `"/oauth/token"` + * `hd` - hosted domain, should be provided only if the user belongs to a Google specific hosted domain. ## Example @@ -147,6 +151,7 @@ defmodule OAuth2.Client do convert to strings. """ @spec put_param(t, String.t | atom, any) :: t + def put_param(%Client{} = client, :hd, ""), do: client def put_param(%Client{params: params} = client, key, value) do %{client | params: Map.put(params, "#{key}", value)} end @@ -195,6 +200,9 @@ defmodule OAuth2.Client do iex> OAuth2.Client.authorize_url!(%OAuth2.Client{}) "/oauth/authorize?client_id=&redirect_uri=&response_type=code" + + iex> OAuth2.Client.authorize_url!(%OAuth2.Client{hd: "example.com"}) + "/oauth/authorize?client_id=&hd=example.com&redirect_uri=&response_type=code" """ @spec authorize_url!(t, list) :: binary def authorize_url!(%Client{} = client, params \\ []) do From f426b89d8848ab3450702b7eaa2f00849bae94dd Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:14:51 +0100 Subject: [PATCH 2/6] Add hd as conditional param to authorizate_url --- lib/oauth2/strategy/auth_code.ex | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/oauth2/strategy/auth_code.ex b/lib/oauth2/strategy/auth_code.ex index a018001..b105921 100644 --- a/lib/oauth2/strategy/auth_code.ex +++ b/lib/oauth2/strategy/auth_code.ex @@ -35,6 +35,7 @@ defmodule OAuth2.Strategy.AuthCode do |> put_param(:response_type, "code") |> put_param(:client_id, client.client_id) |> put_param(:redirect_uri, client.redirect_uri) + |> put_param(:hd, client.hd) |> merge_params(params) end From 8073352ff5bb73a8b5dcfc0b2bf065e60224f501 Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:15:37 +0100 Subject: [PATCH 3/6] Add tests to authorize_url! with hosted domain --- test/oauth2/client_test.exs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/oauth2/client_test.exs b/test/oauth2/client_test.exs index 75f682a..f146486 100644 --- a/test/oauth2/client_test.exs +++ b/test/oauth2/client_test.exs @@ -10,11 +10,13 @@ defmodule OAuth2.ClientTest do setup do server = Bypass.open + client_with_hd = build_client(site: bypass_server(server), hd: "example.com") client = build_client(site: bypass_server(server)) client_with_token = tokenize_client(client) async_client = async_client(client) {:ok, client: client, + client_with_hd: client_with_hd, server: server, client_with_token: client_with_token, async_client: async_client} @@ -30,6 +32,20 @@ defmodule OAuth2.ClientTest do assert query["client_id"] == client.client_id assert query["redirect_uri"] == client.redirect_uri assert query["response_type"] == "code" + assert is_nil(query["hd"]) + end + + test "authorize_url! with hosted domain", %{client_with_hd: client, server: server} do + uri = URI.parse(authorize_url!(client)) + assert "#{uri.scheme}://#{uri.host}:#{uri.port}" == client.site + assert uri.port == server.port + assert uri.path == "/oauth/authorize" + + query = URI.decode_query(uri.query) + assert query["client_id"] == client.client_id + assert query["redirect_uri"] == client.redirect_uri + assert query["response_type"] == "code" + assert query["hd"] == "example.com" end test "get_token, get_token!", %{client: client, server: server} do From 8d7faa777b109161c73c6b53842f16b589d92be8 Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:15:55 +0100 Subject: [PATCH 4/6] Add test to oauth2 supporting hosted domains --- test/oauth2_test.exs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/oauth2_test.exs b/test/oauth2_test.exs index 64a3938..bd51381 100644 --- a/test/oauth2_test.exs +++ b/test/oauth2_test.exs @@ -7,6 +7,12 @@ defmodule OAuth2Test do site: "https://api.github.com", redirect_uri: "http://localhost/auth/callback") + @client_with_hd build_client(client_id: "abc123", + client_secret: "xyz987", + site: "https://api.github.com", + redirect_uri: "http://localhost/auth/callback", + hd: "github.com") + test "`new` delegates to `OAuth2.Client.new/1`" do client = @client assert client.strategy == OAuth2.Strategy.AuthCode @@ -20,6 +26,23 @@ defmodule OAuth2Test do assert client.params == %{} assert client.headers == [] assert client.redirect_uri == "http://localhost/auth/callback" + assert client.hd == "" + end + + test "`new` with hosted domain delegates to `OAuth2.Client.new/1`" do + client = @client_with_hd + assert client.strategy == OAuth2.Strategy.AuthCode + assert client.site == "https://api.github.com" + assert client.client_id == "abc123" + assert client.client_secret == "xyz987" + assert client.site == "https://api.github.com" + assert client.authorize_url == "/oauth/authorize" + assert client.token_url == "/oauth/token" + assert client.hd == "github.com" + assert client.token_method == :post + assert client.params == %{} + assert client.headers == [] + assert client.redirect_uri == "http://localhost/auth/callback" end end From ef56787d43349f26f180db698ba067918a52a190 Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:16:08 +0100 Subject: [PATCH 5/6] Add documentation referring hd as new optional param for client --- lib/oauth2.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/oauth2.ex b/lib/oauth2.ex index b460815..a0a3dd2 100644 --- a/lib/oauth2.ex +++ b/lib/oauth2.ex @@ -20,7 +20,7 @@ defmodule OAuth2 do #### Authorization Code Flow (AuthCode Strategy) - Initialize a client with your `client_id`, `client_secret`, and `site`. + Initialize a client with your `client_id`, `client_secret`, and `site`. Optionally, You may add `hd` to specify your hosted domain when using Google. client = OAuth2.Client.new([ strategy: OAuth2.Strategy.AuthCode, # default strategy is AuthCode From c1fa87d436a72735875a70127d97441a314e2f66 Mon Sep 17 00:00:00 2001 From: Marcelo Lebre Date: Fri, 6 Oct 2017 19:16:21 +0100 Subject: [PATCH 6/6] Update documentation with hosted domain example --- README.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/README.md b/README.md index 5124526..be0ffa4 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,56 @@ case OAuth2.Client.get(client, "/user") do end ``` +### Hosted Domain + +```elixir +# If you want to restrict the user to a specific Google Hosted domain you may initialize the client with `hd`. + +defmodule Google do + use OAuth2.Strategy + + alias OAuth2.Strategy.AuthCode + + def client do + OAuth2.Client.new([ + strategy: __MODULE__, + client_id: System.get_env("GOOGLE_CLIENT_ID"), + client_secret: System.get_env("GOOGLE_CLIENT_SECRET"), + redirect_uri: System.get_env("REDIRECT_URI"), + site: "https://accounts.google.com", + authorize_url: "https://accounts.google.com/o/oauth2/auth", + token_url: "https://accounts.google.com/o/oauth2/token", + hd: System.get_env("YOUR_HOSTED_DOMAIN") + ]) + end + + def authorize_url!(params \\ []) do + OAuth2.Client.authorize_url!(client(), params) + end + + def get_token!(params \\ [], headers \\ []) do + OAuth2.Client.get_token!(client(), params, headers) + end + + # strategy callbacks + + def authorize_url(client, params) do + AuthCode.authorize_url(client, params) + end + + def get_token(client, params, headers) do + client + |> put_param(:client_secret, client.client_secret) + |> put_header("Accept", "application/json") + |> AuthCode.get_token(params, headers) + end +end + +# Generate the authorization URL limited to the Hosted Domain and redirect the user to the provider. +Google.authorize_url!(client) +# => "https://accounts.google.com/o/oauth2/auth?client_id=GOOGLE_CLIENT_ID&hd=unbabel.com&redirect_uri=REDIRECT_URI&response_type=code" +``` + ## Examples - [Authenticate with Github (OAuth2/Phoenix)](https://github.com/scrogson/oauth2_example)