changed README.md
 
@@ -18,42 +18,34 @@ end
18
18
19
19
defp deps do
20
20
# Add the dependency
21
- [{:oauth2, "~> 0.9"}]
21
+ [{:oauth2, "~> 1.0"}]
22
22
end
23
23
```
24
24
25
25
## Configure a serializer
26
26
27
27
This library can be configured to handle encoding and decoding requests and
28
- responses automatically.
28
+ responses automatically based on the `accept` and/or `content-type` headers.
29
29
30
- If you're using [Poison](https://hex.pm/packages/poison) for JSON in your
31
- application, this library is already pre-configured to use it for `"application/json"`
32
- request and response bodies. You will still need to include it as a dependency though.
33
-
34
- If you need to handle different MIME types, you can simply configure it like so:
30
+ If you need to handle various MIME types, you can simply register serializers like so:
35
31
36
32
```elixir
37
- # config/config.exs
38
- config :oauth2,
39
- serializers: %{
40
- "application/vnd.api+json" => Poison,
41
- "application/xml" => MyApp.XmlParser,
42
- }
33
+ OAuth2.Client.put_serializer(client, "application/vnd.api+json", Jason)
34
+ OAuth2.Client.put_serializer(client, "application/xml", MyApp.Parsers.XML)
43
35
```
44
36
45
- The `serializers` option is a map where the keys are MIME types and the values
46
- are modules.
47
-
48
37
The modules are expected to export `encode!/1` and `decode!/1`.
49
38
50
39
```elixir
51
- defmodule MyApp.XmlParser do
40
+ defmodule MyApp.Parsers.XML do
52
41
def encode!(data), do: # ...
53
42
def decode!(binary), do: # ...
54
43
end
55
44
```
56
45
46
+ Please see the documentation for [OAuth2.Serializer](https://hexdocs.pm/oauth2/OAuth2.Serializer.html)
47
+ for more details.
48
+
57
49
## Debug mode
58
50
59
51
Some times its handy to see what's coming back from the response when getting
 
@@ -158,6 +150,7 @@ defmodule GitHub do
158
150
authorize_url: "https://github.com/login/oauth/authorize",
159
151
token_url: "https://github.com/login/oauth/access_token"
160
152
])
153
+ |> OAuth2.Client.put_serializer("application/json", Jason)
161
154
end
162
155
163
156
def authorize_url! do
changed hex_metadata.config
 
@@ -3,15 +3,15 @@
3
3
{<<"description">>,<<"An Elixir OAuth 2.0 Client Library">>}.
4
4
{<<"elixir">>,<<"~> 1.2">>}.
5
5
{<<"files">>,
6
- [<<"lib">>,<<"lib/oauth2">>,<<"lib/oauth2.ex">>,
7
- <<"lib/oauth2/access_token.ex">>,<<"lib/oauth2/client.ex">>,
8
- <<"lib/oauth2/error.ex">>,<<"lib/oauth2/request.ex">>,
9
- <<"lib/oauth2/response.ex">>,<<"lib/oauth2/serializer.ex">>,
10
- <<"lib/oauth2/strategy">>,<<"lib/oauth2/strategy.ex">>,
11
- <<"lib/oauth2/strategy/auth_code.ex">>,
6
+ [<<"lib">>,<<"lib/oauth2">>,<<"lib/oauth2/serializer.ex">>,
7
+ <<"lib/oauth2/client.ex">>,<<"lib/oauth2/access_token.ex">>,
8
+ <<"lib/oauth2/strategy">>,<<"lib/oauth2/strategy/password.ex">>,
9
+ <<"lib/oauth2/strategy/refresh.ex">>,
12
10
<<"lib/oauth2/strategy/client_credentials.ex">>,
13
- <<"lib/oauth2/strategy/password.ex">>,<<"lib/oauth2/strategy/refresh.ex">>,
14
- <<"lib/oauth2/util.ex">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
11
+ <<"lib/oauth2/strategy/auth_code.ex">>,<<"lib/oauth2/strategy.ex">>,
12
+ <<"lib/oauth2/error.ex">>,<<"lib/oauth2/request.ex">>,
13
+ <<"lib/oauth2/response.ex">>,<<"lib/oauth2/util.ex">>,<<"lib/oauth2.ex">>,
14
+ <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>]}.
15
15
{<<"licenses">>,[<<"MIT">>]}.
16
16
{<<"links">>,[{<<"github">>,<<"https://github.com/scrogson/oauth2">>}]}.
17
17
{<<"name">>,<<"oauth2">>}.
 
@@ -20,5 +20,5 @@
20
20
{<<"name">>,<<"hackney">>},
21
21
{<<"optional">>,false},
22
22
{<<"repository">>,<<"hexpm">>},
23
- {<<"requirement">>,<<"~> 1.7">>}]]}.
24
- {<<"version">>,<<"0.9.4">>}.
23
+ {<<"requirement">>,<<"~> 1.13.0">>}]]}.
24
+ {<<"version">>,<<"1.0.0">>}.
changed lib/oauth2/client.ex
 
@@ -41,6 +41,7 @@ defmodule OAuth2.Client do
41
41
@type redirect_uri :: binary
42
42
@type ref :: reference | nil
43
43
@type request_opts :: Keyword.t
44
+ @type serializers :: %{binary => module}
44
45
@type site :: binary
45
46
@type strategy :: module
46
47
@type token :: AccessToken.t | nil
 
@@ -56,6 +57,7 @@ defmodule OAuth2.Client do
56
57
redirect_uri: redirect_uri,
57
58
ref: ref,
58
59
request_opts: request_opts,
60
+ serializers: serializers,
59
61
site: site,
60
62
strategy: strategy,
61
63
token: token,
 
@@ -71,6 +73,7 @@ defmodule OAuth2.Client do
71
73
redirect_uri: "",
72
74
ref: nil,
73
75
request_opts: [],
76
+ serializers: %{},
74
77
site: "",
75
78
strategy: OAuth2.Strategy.AuthCode,
76
79
token: nil,
 
@@ -179,7 +182,9 @@ defmodule OAuth2.Client do
179
182
@spec put_headers(t, list) :: t
180
183
def put_headers(%Client{} = client, []), do: client
181
184
def put_headers(%Client{} = client, [{k,v}|rest]) do
182
- client |> put_header(k,v) |> put_headers(rest)
185
+ client
186
+ |> put_header(k, v)
187
+ |> put_headers(rest)
183
188
end
184
189
185
190
@doc false
 
@@ -202,6 +207,43 @@ defmodule OAuth2.Client do
202
207
url
203
208
end
204
209
210
+ @doc """
211
+ Register a serialization module for a given mime type.
212
+
213
+ ## Example
214
+
215
+ iex> client = OAuth2.Client.put_serializer(%OAuth2.Client{}, "application/json", Jason)
216
+ %OAuth2.Client{serializers: %{"application/json" => Jason}}
217
+ iex> OAuth2.Client.get_serializer(client, "application/json")
218
+ Jason
219
+ """
220
+ @spec put_serializer(t, binary, atom) :: t
221
+ def put_serializer(%Client{serializers: serializers} = client, mime, module)
222
+ when is_binary(mime) and is_atom(module) do
223
+ %Client{client | serializers: Map.put(serializers, mime, module)}
224
+ end
225
+
226
+ @doc """
227
+ Un-register a serialization module for a given mime type.
228
+
229
+ ## Example
230
+
231
+ iex> client = OAuth2.Client.delete_serializer(%OAuth2.Client{}, "application/json")
232
+ %OAuth2.Client{}
233
+ iex> OAuth2.Client.get_serializer(client, "application/json")
234
+ nil
235
+ """
236
+ @spec delete_serializer(t, binary) :: t
237
+ def delete_serializer(%Client{serializers: serializers} = client, mime) do
238
+ %Client{client | serializers: Map.delete(serializers, mime)}
239
+ end
240
+
241
+ @doc false
242
+ @spec get_serializer(t, binary) :: atom
243
+ def get_serializer(%Client{serializers: serializers}, mime) do
244
+ Map.get(serializers, mime)
245
+ end
246
+
205
247
@doc """
206
248
Fetches an `OAuth2.AccessToken` struct by making a request to the token endpoint.
changed lib/oauth2/request.ex
 
@@ -1,9 +1,10 @@
1
1
defmodule OAuth2.Request do
2
2
@moduledoc false
3
3
4
+ require Logger
4
5
import OAuth2.Util
5
6
6
- alias OAuth2.{Client, Error, Response, Serializer}
7
+ alias OAuth2.{Client, Error, Response}
7
8
8
9
@type body :: any
9
10
 
@@ -16,17 +17,29 @@ defmodule OAuth2.Request do
16
17
url = client |> process_url(url) |> process_params(opts[:params])
17
18
headers = req_headers(client, headers) |> Enum.uniq
18
19
content_type = content_type(headers)
19
- body = encode_request_body(body, content_type)
20
+ serializer = Client.get_serializer(client, content_type)
21
+ body = encode_request_body(body, content_type, serializer)
20
22
headers = process_request_headers(headers, content_type)
21
23
req_opts = Keyword.merge(client.request_opts, opts)
22
24
25
+ if Application.get_env(:oauth2, :debug) do
26
+ Logger.debug("""
27
+ OAuth2 Provider Request
28
+ url: #{inspect url}
29
+ method: #{inspect method}
30
+ headers: #{inspect headers}
31
+ body: #{inspect body}
32
+ req_opts: #{inspect req_opts}
33
+ """)
34
+ end
35
+
23
36
case :hackney.request(method, url, headers, body, req_opts) do
24
37
{:ok, ref} when is_reference(ref) ->
25
38
{:ok, ref}
26
39
{:ok, status, headers, ref} when is_reference(ref) ->
27
- process_body(status, headers, ref)
40
+ process_body(client, status, headers, ref)
28
41
{:ok, status, headers, body} when is_binary(body) ->
29
- process_body(status, headers, body)
42
+ process_body(client, status, headers, body)
30
43
{:error, reason} ->
31
44
{:error, %Error{reason: reason}}
32
45
end
 
@@ -68,16 +81,16 @@ defmodule OAuth2.Request do
68
81
end
69
82
end
70
83
71
- defp process_body(status, headers, ref) when is_reference(ref) do
84
+ defp process_body(client, status, headers, ref) when is_reference(ref) do
72
85
case :hackney.body(ref) do
73
86
{:ok, body} ->
74
- process_body(status, headers, body)
87
+ process_body(client, status, headers, body)
75
88
{:error, reason} ->
76
89
{:error, %Error{reason: reason}}
77
90
end
78
91
end
79
- defp process_body(status, headers, body) when is_binary(body) do
80
- resp = Response.new(status, headers, body)
92
+ defp process_body(client, status, headers, body) when is_binary(body) do
93
+ resp = Response.new(client, status, headers, body)
81
94
case status do
82
95
status when status in 200..399 ->
83
96
{:ok, resp}
 
@@ -108,9 +121,14 @@ defmodule OAuth2.Request do
108
121
end
109
122
end
110
123
111
- defp encode_request_body("", _), do: ""
112
- defp encode_request_body([], _), do: ""
113
- defp encode_request_body(body, "application/x-www-form-urlencoded"),
124
+ defp encode_request_body("", _, _), do: ""
125
+ defp encode_request_body([], _, _), do: ""
126
+ defp encode_request_body(body, "application/x-www-form-urlencoded", _),
114
127
do: URI.encode_query(body)
115
- defp encode_request_body(body, type), do: Serializer.encode!(body, type)
128
+ defp encode_request_body(body, _mime, nil) do
129
+ body
130
+ end
131
+ defp encode_request_body(body, _mime, serializer) do
132
+ serializer.encode!(body)
133
+ end
116
134
end
changed lib/oauth2/response.ex
 
@@ -12,7 +12,7 @@ defmodule OAuth2.Response do
12
12
13
13
require Logger
14
14
import OAuth2.Util
15
- alias OAuth2.Serializer
15
+ alias OAuth2.Client
16
16
17
17
@type status_code :: integer
18
18
@type headers :: list
 
@@ -27,9 +27,11 @@ defmodule OAuth2.Response do
27
27
defstruct status_code: nil, headers: [], body: nil
28
28
29
29
@doc false
30
- def new(code, headers, body) do
30
+ def new(client, code, headers, body) do
31
31
headers = process_headers(headers)
32
- body = decode_response_body(body, content_type(headers))
32
+ content_type = content_type(headers)
33
+ serializer = Client.get_serializer(client, content_type)
34
+ body = decode_response_body(body, content_type, serializer)
33
35
resp = %__MODULE__{status_code: code, headers: headers, body: body}
34
36
35
37
if Application.get_env(:oauth2, :debug) do
 
@@ -43,16 +45,22 @@ defmodule OAuth2.Response do
43
45
Enum.map(headers, fn {k, v} -> {String.downcase(k), v} end)
44
46
end
45
47
46
- defp decode_response_body("", _type), do: ""
47
- defp decode_response_body(" ", _type), do: ""
48
+ defp decode_response_body("", _type, _), do: ""
49
+ defp decode_response_body(" ", _type, _), do: ""
48
50
# Facebook sends text/plain tokens!?
49
- defp decode_response_body(body, "text/plain") do
51
+ defp decode_response_body(body, "text/plain", _) do
50
52
case URI.decode_query(body) do
51
53
%{"access_token" => _} = token -> token
52
54
_ -> body
53
55
end
54
56
end
55
- defp decode_response_body(body, "application/x-www-form-urlencoded"),
56
- do: URI.decode_query(body)
57
- defp decode_response_body(body, type), do: Serializer.decode!(body, type)
57
+ defp decode_response_body(body, "application/x-www-form-urlencoded", _) do
58
+ URI.decode_query(body)
59
+ end
60
+ defp decode_response_body(body, _mime, nil) do
61
+ body
62
+ end
63
+ defp decode_response_body(body, _type, serializer) do
64
+ serializer.decode!(body)
65
+ end
58
66
end
changed lib/oauth2/serializer.ex
 
@@ -1,50 +1,15 @@
1
1
defmodule OAuth2.Serializer do
2
- @moduledoc false
2
+ @moduledoc """
3
+ A serializer is responsible for encoding/decoding request/response bodies.
3
4
4
- require Logger
5
+ ## Example
5
6
6
- defmodule NullSerializer do
7
- @moduledoc false
7
+ defmodule MyApp.JSON do
8
+ def encode!(data), do: Jason.encode!(data)
9
+ def decode!(binary), do: Jason.decode!(binary)
10
+ end
11
+ """
8
12
9
- @doc false
10
- def decode!(content), do: content
11
-
12
- @doc false
13
- def encode!(content), do: content
14
- end
15
-
16
- def decode!(content, type), do: serializer(type).decode!(content)
17
-
18
- def encode!(content, type), do: serializer(type).encode!(content)
19
-
20
- defp serializer(type) do
21
- serializer = Map.get(configured_serializers(), type, NullSerializer)
22
- warn_missing_serializer = Application.get_env(:oauth2, :warn_missing_serializer, true)
23
-
24
- if serializer == NullSerializer && warn_missing_serializer do
25
- Logger.warn """
26
-
27
- A serializer was not configured for content-type '#{type}'.
28
-
29
- To remove this warning for this content-type, add the following to your `config.exs` file:
30
-
31
- config :oauth2,
32
- serializers: %{
33
- "#{type}" => MySerializer
34
- }
35
-
36
- To remove this warning entirely, add the following to you `config.exs` file:
37
-
38
- config :oauth2,
39
- warn_missing_serializer: false
40
- """
41
- end
42
-
43
- serializer
44
- end
45
-
46
- defp configured_serializers do
47
- Application.get_env(:oauth2, :serializers) ||
48
- raise("Missing serializers configuration! Make sure oauth2 app is added to mix application list")
49
- end
13
+ @callback encode!(map) :: binary
14
+ @callback decode!(binary) :: map
50
15
end
changed lib/oauth2/strategy.ex
 
@@ -100,6 +100,7 @@ defmodule OAuth2.Strategy do
100
100
101
101
defmacro __using__(_) do
102
102
quote do
103
+ @behaviour OAuth2.Strategy
103
104
import OAuth2.Client
104
105
end
105
106
end
changed mix.exs
 
@@ -1,7 +1,7 @@
1
1
defmodule OAuth2.Mixfile do
2
2
use Mix.Project
3
3
4
- @version "0.9.4"
4
+ @version "1.0.0"
5
5
6
6
def project do
7
7
[app: :oauth2,
 
@@ -22,17 +22,17 @@ defmodule OAuth2.Mixfile do
22
22
end
23
23
24
24
def application do
25
- [applications: [:logger, :hackney],
26
- env: [serializers: %{"application/json" => Poison}]]
25
+ [applications: [:logger, :hackney]]
27
26
end
28
27
29
28
defp deps do
30
- [{:hackney, "~> 1.7"},
29
+ [{:hackney, "~> 1.13.0"},
31
30
32
31
# Test dependencies
33
- {:poison, "~> 3.0", only: :test},
34
- {:bypass, "~> 0.6", only: :test},
35
- {:excoveralls, "~> 0.5", only: :test},
32
+ {:jason, "~> 1.0", only: :test},
33
+ {:bypass, "~> 0.9", only: :test},
34
+ {:plug_cowboy, "~> 1.0", only: :test},
35
+ {:excoveralls, "~> 0.9", only: :test},
36
36
{:dialyxir, "~> 0.5", only: [:dev], runtime: false},
37
37
38
38
# Docs dependencies