From d0bd63bf854426db5fbeedada3dcfbc2be44c15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Stordahl?= Date: Tue, 10 Nov 2020 20:17:40 +0100 Subject: [PATCH 1/2] Support for userinfo endpoint --- lib/openid_connect.ex | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/openid_connect.ex b/lib/openid_connect.ex index a532724..4439584 100644 --- a/lib/openid_connect.ex +++ b/lib/openid_connect.ex @@ -185,6 +185,25 @@ defmodule OpenIDConnect do end end + @spec fetch_userinfo(provider, String.t(), name) :: success(map) | error(:fetch_userinfo) + @doc """ + Fetches the information that the user has consented to share + """ + def fetch_userinfo(provider, access_token, name \\ :openid_connect) do + uri = userinfo_uri(provider, name) + headers = [{"Authorization", "Bearer #{access_token}"}] + + with {:ok, %HTTPoison.Response{status_code: status_code} = resp} when status_code in 200..299 <- + http_client().get(uri, headers, http_client_options()), + {:ok, json} <- Jason.decode(resp.body), + {:ok, json} <- assert_json(json) do + {:ok, json} + else + {:ok, resp} -> {:error, :fetch_tokens, resp} + {:error, reason} -> {:error, :fetch_tokens, reason} + end + end + @spec update_documents(list) :: success(documents) | error(:update_documents) @doc """ Requests updated documents from the provider @@ -282,6 +301,10 @@ defmodule OpenIDConnect do Map.get(discovery_document(provider, name), "token_endpoint") end + defp userinfo_uri(provider, name) do + Map.get(discovery_document(provider, name), "userinfo_endpoint") + end + defp client_id(config) do Keyword.get(config, :client_id) end From e38ffb361bd3c89df8e703816545e13306e29b09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A5kon=20Stordahl?= Date: Thu, 17 Dec 2020 10:13:12 +0100 Subject: [PATCH 2/2] Add test for userinfo endpoint --- test/fixtures/google/userinfo.exs | 32 +++++++++++++++++++++++++++++++ test/openid_connect_test.exs | 30 +++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 test/fixtures/google/userinfo.exs diff --git a/test/fixtures/google/userinfo.exs b/test/fixtures/google/userinfo.exs new file mode 100644 index 0000000..afb4133 --- /dev/null +++ b/test/fixtures/google/userinfo.exs @@ -0,0 +1,32 @@ +%HTTPoison.Response{ + body: %{ + "sub" => "353690423699814251281", + "name" => "Ada Lovelace", + "given_name" => "Ada", + "family_name" => "Lovelace", + "picture" => + "https://lh3.googleusercontent.com/-XdUIqdMkCWA/AAAAAAAAAAI/AAAAAAAAAAA/4252rscbv5M/photo.jpg", + "email" => "ada@example.com", + "email_verified" => true, + "locale" => "en" + }, + headers: [ + {"Date", "Thu, 17 Dec 2020 14:29:16 GMT"}, + {"Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"}, + {"Expires", "Mon, 01 Jan 1990 00:00:00 GMT"}, + {"Pragma", "no-cache"}, + {"Content-Type", "application/json; charset=utf-8"}, + {"Vary", "X-Origin"}, + {"Vary", "Referer"}, + {"Server", "ESF"}, + {"X-XSS-Protection", "0"}, + {"X-Frame-Options", "SAMEORIGIN"}, + {"X-Content-Type-Options", "nosniff"}, + {"Alt-Svc", + "h3-29=\":443\"; ma=2592000,h3-T051=\":443\"; ma=2592000,h3-Q050=\":443\"; ma=2592000,h3-Q046=\":443\"; ma=2592000,h3-Q043=\":443\"; ma=2592000,quic=\":443\"; ma=2592000; v=\"46,43\""}, + {"Accept-Ranges", "none"}, + {"Vary", "Origin,Accept-Encoding"}, + {"Transfer-Encoding", "chunked"} + ], + status_code: 200 +} diff --git a/test/openid_connect_test.exs b/test/openid_connect_test.exs index b964560..5568ccc 100644 --- a/test/openid_connect_test.exs +++ b/test/openid_connect_test.exs @@ -8,6 +8,7 @@ defmodule OpenIDConnectTest do @google_document Fixtures.load(:google, :discovery_document) @google_certs Fixtures.load(:google, :certs) + @google_userinfo Fixtures.load(:google, :userinfo) alias OpenIDConnect.{HTTPClientMock, MockWorker} @@ -526,6 +527,35 @@ defmodule OpenIDConnectTest do end end + describe "fetch userinfo" do + test "when successful" do + {:ok, pid} = GenServer.start_link(MockWorker, [], name: :openid_connect) + + access_token = "mF_9.B5f-4.1JqM" + headers = [{"Authorization", "Bearer #{access_token}"}] + + expected_userinfo = + @google_userinfo + |> elem(1) + |> Map.get(:body) + |> Jason.decode!() + + try do + expect(HTTPClientMock, :get, fn "https://www.googleapis.com/oauth2/v3/userinfo", + ^headers, + _opts -> + @google_userinfo + end) + + {:ok, body} = OpenIDConnect.fetch_userinfo(:google, access_token) + + assert expected_userinfo == body + after + GenServer.stop(pid) + end + end + end + defp set_jose_json_lib(_) do JOSE.json_module(JasonEncoder) []