changed README.md
 
@@ -18,7 +18,7 @@ end
18
18
19
19
defp deps do
20
20
# Add the dependency
21
- [{:oauth2, "~> 1.0"}]
21
+ [{:oauth2, "~> 2.0"}]
22
22
end
23
23
```
24
24
 
@@ -48,7 +48,7 @@ for more details.
48
48
49
49
## Debug mode
50
50
51
- Some times its handy to see what's coming back from the response when getting
51
+ Sometimes it's handy to see what's coming back from the response when getting
52
52
a token. You can configure OAuth2 to output the response like so:
53
53
54
54
```elixir
 
@@ -170,7 +170,6 @@ defmodule GitHub do
170
170
171
171
def get_token(client, params, headers) do
172
172
client
173
- |> put_param(:client_secret, client.client_secret)
174
173
|> put_header("accept", "application/json")
175
174
|> OAuth2.Strategy.AuthCode.get_token(params, headers)
176
175
end
changed hex_metadata.config
 
@@ -21,4 +21,4 @@
21
21
{<<"optional">>,false},
22
22
{<<"repository">>,<<"hexpm">>},
23
23
{<<"requirement">>,<<"~> 1.13">>}]]}.
24
- {<<"version">>,<<"1.0.1">>}.
24
+ {<<"version">>,<<"2.0.0">>}.
changed lib/oauth2.ex
 
@@ -2,7 +2,7 @@ defmodule OAuth2 do
2
2
@moduledoc """
3
3
The OAuth2 specification
4
4
5
- https://tools.ietf.org/html/rfc6749
5
+ [RFC6749](https://tools.ietf.org/html/rfc6749)
6
6
7
7
The OAuth 2.0 authorization framework enables a third-party
8
8
application to obtain limited access to an HTTP service, either on
 
@@ -12,13 +12,13 @@ defmodule OAuth2 do
12
12
13
13
## API
14
14
15
- Current implemented strategies:
15
+ Currently implemented strategies:
16
16
17
17
- Authorization Code
18
18
- Password
19
19
- Client Credentials
20
20
21
- #### Authorization Code Flow (AuthCode Strategy)
21
+ ### Authorization Code Flow (AuthCode Strategy)
22
22
23
23
Initialize a client with your `client_id`, `client_secret`, and `site`.
changed lib/oauth2/access_token.ex
 
@@ -14,19 +14,20 @@ defmodule OAuth2.AccessToken do
14
14
15
15
@standard ["access_token", "refresh_token", "expires_in", "token_type"]
16
16
17
- @type access_token :: binary
17
+ @type access_token :: binary
18
18
@type refresh_token :: binary | nil
19
- @type expires_at :: integer
20
- @type token_type :: binary
21
- @type other_params :: %{binary => binary}
22
- @type body :: binary | map
19
+ @type expires_at :: integer
20
+ @type token_type :: binary
21
+ @type other_params :: %{binary => binary}
22
+ @type body :: binary | map | list
23
23
24
24
@type t :: %__MODULE__{
25
- access_token: access_token,
26
- refresh_token: refresh_token,
27
- expires_at: expires_at,
28
- token_type: token_type,
29
- other_params: other_params}
25
+ access_token: access_token,
26
+ refresh_token: refresh_token,
27
+ expires_at: expires_at,
28
+ token_type: token_type,
29
+ other_params: other_params
30
+ }
30
31
31
32
defstruct access_token: "",
32
33
refresh_token: nil,
 
@@ -58,13 +59,13 @@ defmodule OAuth2.AccessToken do
58
59
def new(response) when is_map(response) do
59
60
{std, other} = Map.split(response, @standard)
60
61
61
- struct(AccessToken, [
62
- access_token: std["access_token"],
62
+ struct(AccessToken,
63
+ access_token: std["access_token"],
63
64
refresh_token: std["refresh_token"],
64
- expires_at: (std["expires_in"] || other["expires"]) |> expires_at,
65
- token_type: std["token_type"] |> normalize_token_type(),
66
- other_params: other
67
- ])
65
+ expires_at: (std["expires_in"] || other["expires"]) |> expires_at,
66
+ token_type: std["token_type"] |> normalize_token_type(),
67
+ other_params: other
68
+ )
68
69
end
69
70
70
71
@doc """
 
@@ -72,7 +73,7 @@ defmodule OAuth2.AccessToken do
72
73
73
74
Returns `true` unless `expires_at` is `nil`.
74
75
"""
75
- @spec expires?(AccessToken.t) :: boolean
76
+ @spec expires?(AccessToken.t()) :: boolean
76
77
def expires?(%AccessToken{expires_at: nil} = _token), do: false
77
78
def expires?(_), do: true
78
79
 
@@ -87,12 +88,14 @@ defmodule OAuth2.AccessToken do
87
88
Returns a unix timestamp based on now + expires_at (in seconds).
88
89
"""
89
90
def expires_at(nil), do: nil
91
+
90
92
def expires_at(val) when is_binary(val) do
91
93
val
92
- |> Integer.parse
94
+ |> Integer.parse()
93
95
|> elem(0)
94
96
|> expires_at
95
97
end
98
+
96
99
def expires_at(int), do: unix_now() + int
97
100
98
101
defp normalize_token_type(nil), do: "Bearer"
changed lib/oauth2/client.ex
 
@@ -29,41 +29,41 @@ defmodule OAuth2.Client do
29
29
response = OAuth2.Client.post!(client, "/some/other/resources", %{foo: "bar"})
30
30
"""
31
31
32
- alias OAuth2.{AccessToken, Client, Error, Response, Request}
32
+ alias OAuth2.{AccessToken, Client, Error, Request, Response}
33
33
34
34
@type authorize_url :: binary
35
- @type body :: any
36
- @type client_id :: binary
35
+ @type body :: any
36
+ @type client_id :: binary
37
37
@type client_secret :: binary
38
- @type headers :: [{binary, binary}]
39
- @type param :: binary | %{binary => param} | [param]
40
- @type params :: %{binary => param} | Keyword.t
41
- @type redirect_uri :: binary
42
- @type ref :: reference | nil
43
- @type request_opts :: Keyword.t
44
- @type serializers :: %{binary => module}
45
- @type site :: binary
46
- @type strategy :: module
47
- @type token :: AccessToken.t | nil
48
- @type token_method :: :post | :get | atom
49
- @type token_url :: binary
38
+ @type headers :: [{binary, binary}]
39
+ @type param :: binary | %{binary => param} | [param]
40
+ @type params :: %{binary => param} | Keyword.t()
41
+ @type redirect_uri :: binary
42
+ @type ref :: reference | nil
43
+ @type request_opts :: Keyword.t()
44
+ @type serializers :: %{binary => module}
45
+ @type site :: binary
46
+ @type strategy :: module
47
+ @type token :: AccessToken.t() | nil
48
+ @type token_method :: :post | :get | atom
49
+ @type token_url :: binary
50
50
51
51
@type t :: %Client{
52
- authorize_url: authorize_url,
53
- client_id: client_id,
54
- client_secret: client_secret,
55
- headers: headers,
56
- params: params,
57
- redirect_uri: redirect_uri,
58
- ref: ref,
59
- request_opts: request_opts,
60
- serializers: serializers,
61
- site: site,
62
- strategy: strategy,
63
- token: token,
64
- token_method: token_method,
65
- token_url: token_url
66
- }
52
+ authorize_url: authorize_url,
53
+ client_id: client_id,
54
+ client_secret: client_secret,
55
+ headers: headers,
56
+ params: params,
57
+ redirect_uri: redirect_uri,
58
+ ref: ref,
59
+ request_opts: request_opts,
60
+ serializers: serializers,
61
+ site: site,
62
+ strategy: strategy,
63
+ token: token,
64
+ token_method: token_method,
65
+ token_url: token_url
66
+ }
67
67
68
68
defstruct authorize_url: "/oauth/authorize",
69
69
client_id: "",
 
@@ -126,7 +126,7 @@ defmodule OAuth2.Client do
126
126
127
127
[hackney documentation]: https://github.com/benoitc/hackney/blob/master/doc/hackney.md#request5
128
128
"""
129
- @spec new(t, Keyword.t) :: t
129
+ @spec new(t, Keyword.t()) :: t
130
130
def new(client \\ %Client{}, opts) do
131
131
{token, opts} = Keyword.pop(opts, :token)
132
132
{req_opts, opts} = Keyword.pop(opts, :request_opts, [])
 
@@ -149,7 +149,7 @@ defmodule OAuth2.Client do
149
149
The key can be a `string` or an `atom`. Atoms are automatically
150
150
convert to strings.
151
151
"""
152
- @spec put_param(t, String.t | atom, any) :: t
152
+ @spec put_param(t, String.t() | atom, any) :: t
153
153
def put_param(%Client{params: params} = client, key, value) do
154
154
%{client | params: Map.put(params, "#{key}", value)}
155
155
end
 
@@ -159,9 +159,11 @@ defmodule OAuth2.Client do
159
159
"""
160
160
@spec merge_params(t, params) :: t
161
161
def merge_params(client, params) do
162
- params = Enum.reduce(params, %{}, fn {k,v}, acc ->
163
- Map.put(acc, "#{k}", v)
164
- end)
162
+ params =
163
+ Enum.reduce(params, %{}, fn {k, v}, acc ->
164
+ Map.put(acc, "#{k}", v)
165
+ end)
166
+
165
167
%{client | params: Map.merge(client.params, params)}
166
168
end
167
169
 
@@ -171,7 +173,7 @@ defmodule OAuth2.Client do
171
173
"""
172
174
@spec put_header(t, binary, binary) :: t
173
175
def put_header(%Client{headers: headers} = client, key, value)
174
- when is_binary(key) and is_binary(value) do
176
+ when is_binary(key) and is_binary(value) do
175
177
key = String.downcase(key)
176
178
%{client | headers: List.keystore(headers, key, 0, {key, value})}
177
179
end
 
@@ -181,7 +183,8 @@ defmodule OAuth2.Client do
181
183
"""
182
184
@spec put_headers(t, list) :: t
183
185
def put_headers(%Client{} = client, []), do: client
184
- def put_headers(%Client{} = client, [{k,v}|rest]) do
186
+
187
+ def put_headers(%Client{} = client, [{k, v} | rest]) do
185
188
client
186
189
|> put_header(k, v)
187
190
|> put_headers(rest)
 
@@ -219,7 +222,7 @@ defmodule OAuth2.Client do
219
222
"""
220
223
@spec put_serializer(t, binary, atom) :: t
221
224
def put_serializer(%Client{serializers: serializers} = client, mime, module)
222
- when is_binary(mime) and is_atom(module) do
225
+ when is_binary(mime) and is_atom(module) do
223
226
%Client{client | serializers: Map.put(serializers, mime, module)}
224
227
end
225
228
 
@@ -266,7 +269,8 @@ defmodule OAuth2.Client do
266
269
* `:proxy` - a proxy to be used for the request; it can be a regular url or a
267
270
`{host, proxy}` tuple
268
271
"""
269
- @spec get_token(t, params, headers, Keyword.t) :: {:ok, Client.t} | {:error, Response.t} | {:error, Error.t}
272
+ @spec get_token(t, params, headers, Keyword.t()) ::
273
+ {:ok, Client.t()} | {:error, Response.t()} | {:error, Error.t()}
270
274
def get_token(%{token_method: method} = client, params \\ [], headers \\ [], opts \\ []) do
271
275
{client, url} = token_url(client, params, headers)
272
276
 
@@ -274,6 +278,7 @@ defmodule OAuth2.Client do
274
278
{:ok, response} ->
275
279
token = AccessToken.new(response.body)
276
280
{:ok, %{client | headers: [], params: %{}, token: token}}
281
+
277
282
{:error, error} ->
278
283
{:error, error}
279
284
end
 
@@ -283,22 +288,26 @@ defmodule OAuth2.Client do
283
288
Same as `get_token/4` but raises `OAuth2.Error` if an error occurs during the
284
289
request.
285
290
"""
286
- @spec get_token!(t, params, headers, Keyword.t) :: Client.t | Error.t
291
+ @spec get_token!(t, params, headers, Keyword.t()) :: Client.t() | Error.t()
287
292
def get_token!(client, params \\ [], headers \\ [], opts \\ []) do
288
293
case get_token(client, params, headers, opts) do
289
294
{:ok, client} ->
290
295
client
296
+
291
297
{:error, %Response{status_code: code, headers: headers, body: body}} ->
292
- raise %Error{reason: """
293
- Server responded with status: #{code}
298
+ raise %Error{
299
+ reason: """
300
+ Server responded with status: #{code}
294
301
295
- Headers:
302
+ Headers:
296
303
297
- #{Enum.reduce(headers, "", fn {k, v}, acc -> acc <> "#{k}: #{v}\n" end)}
298
- Body:
304
+ #{Enum.reduce(headers, "", fn {k, v}, acc -> acc <> "#{k}: #{v}\n" end)}
305
+ Body:
306
+
307
+ #{inspect(body)}
308
+ """
309
+ }
299
310
300
- #{inspect body}
301
- """}
302
311
{:error, error} ->
303
312
raise error
304
313
end
 
@@ -307,12 +316,20 @@ defmodule OAuth2.Client do
307
316
@doc """
308
317
Refreshes an existing access token using a refresh token.
309
318
"""
310
- @spec refresh_token(t, params, headers, Keyword.t) :: {:ok, Client.t} | {:error, Response.t} | {:error, Error.t}
319
+ @spec refresh_token(t, params, headers, Keyword.t()) ::
320
+ {:ok, Client.t()} | {:error, Response.t()} | {:error, Error.t()}
311
321
def refresh_token(token, params \\ [], headers \\ [], opts \\ [])
322
+
312
323
def refresh_token(%Client{token: %{refresh_token: nil}}, _params, _headers, _opts) do
313
324
{:error, %Error{reason: "Refresh token not available."}}
314
325
end
315
- def refresh_token(%Client{token: %{refresh_token: refresh_token}} = client, params, headers, opts) do
326
+
327
+ def refresh_token(
328
+ %Client{token: %{refresh_token: refresh_token}} = client,
329
+ params,
330
+ headers,
331
+ opts
332
+ ) do
316
333
refresh_client =
317
334
%{client | strategy: OAuth2.Strategy.Refresh, token: nil}
318
335
|> Client.put_param(:refresh_token, refresh_token)
 
@@ -324,14 +341,16 @@ defmodule OAuth2.Client do
324
341
else
325
342
{:ok, put_in(client.token.refresh_token, refresh_token)}
326
343
end
327
- {:error, error} -> {:error, error}
344
+
345
+ {:error, error} ->
346
+ {:error, error}
328
347
end
329
348
end
330
349
331
350
@doc """
332
351
Calls `refresh_token/4` but raises `Error` if there an error occurs.
333
352
"""
334
- @spec refresh_token!(t, params, headers, Keyword.t) :: Client.t | Error.t
353
+ @spec refresh_token!(t, params, headers, Keyword.t()) :: Client.t() | Error.t()
335
354
def refresh_token!(%Client{} = client, params \\ [], headers \\ [], opts \\ []) do
336
355
case refresh_token(client, params, headers, opts) do
337
356
{:ok, %Client{} = client} -> client
 
@@ -351,7 +370,8 @@ defmodule OAuth2.Client do
351
370
Makes a `GET` request to the given `url` using the `OAuth2.AccessToken`
352
371
struct.
353
372
"""
354
- @spec get(t, binary, headers, Keyword.t) :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
373
+ @spec get(t, binary, headers, Keyword.t()) ::
374
+ {:ok, Response.t()} | {:error, Response.t()} | {:error, Error.t()}
355
375
def get(%Client{} = client, url, headers \\ [], opts \\ []),
356
376
do: Request.request(:get, client, url, "", headers, opts)
357
377
 
@@ -359,7 +379,7 @@ defmodule OAuth2.Client do
359
379
Same as `get/4` but returns a `OAuth2.Response` or `OAuth2.Error` exception if
360
380
the request results in an error.
361
381
"""
362
- @spec get!(t, binary, headers, Keyword.t) :: Response.t | Error.t
382
+ @spec get!(t, binary, headers, Keyword.t()) :: Response.t() | Error.t()
363
383
def get!(%Client{} = client, url, headers \\ [], opts \\ []),
364
384
do: Request.request!(:get, client, url, "", headers, opts)
365
385
 
@@ -367,7 +387,8 @@ defmodule OAuth2.Client do
367
387
Makes a `PUT` request to the given `url` using the `OAuth2.AccessToken`
368
388
struct.
369
389
"""
370
- @spec put(t, binary, body, headers, Keyword.t) :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
390
+ @spec put(t, binary, body, headers, Keyword.t()) ::
391
+ {:ok, Response.t()} | {:error, Response.t()} | {:error, Error.t()}
371
392
def put(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
372
393
do: Request.request(:put, client, url, body, headers, opts)
373
394
 
@@ -378,7 +399,7 @@ defmodule OAuth2.Client do
378
399
An `OAuth2.Error` exception is raised if the request results in an
379
400
error tuple (`{:error, reason}`).
380
401
"""
381
- @spec put!(t, binary, body, headers, Keyword.t) :: Response.t | Error.t
402
+ @spec put!(t, binary, body, headers, Keyword.t()) :: Response.t() | Error.t()
382
403
def put!(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
383
404
do: Request.request!(:put, client, url, body, headers, opts)
384
405
 
@@ -386,7 +407,8 @@ defmodule OAuth2.Client do
386
407
Makes a `PATCH` request to the given `url` using the `OAuth2.AccessToken`
387
408
struct.
388
409
"""
389
- @spec patch(t, binary, body, headers, Keyword.t) :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
410
+ @spec patch(t, binary, body, headers, Keyword.t()) ::
411
+ {:ok, Response.t()} | {:error, Response.t()} | {:error, Error.t()}
390
412
def patch(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
391
413
do: Request.request(:patch, client, url, body, headers, opts)
392
414
 
@@ -397,14 +419,15 @@ defmodule OAuth2.Client do
397
419
An `OAuth2.Error` exception is raised if the request results in an
398
420
error tuple (`{:error, reason}`).
399
421
"""
400
- @spec patch!(t, binary, body, headers, Keyword.t) :: Response.t | Error.t
422
+ @spec patch!(t, binary, body, headers, Keyword.t()) :: Response.t() | Error.t()
401
423
def patch!(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
402
424
do: Request.request!(:patch, client, url, body, headers, opts)
403
425
404
426
@doc """
405
427
Makes a `POST` request to the given URL using the `OAuth2.AccessToken`.
406
428
"""
407
- @spec post(t, binary, body, headers, Keyword.t) :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
429
+ @spec post(t, binary, body, headers, Keyword.t()) ::
430
+ {:ok, Response.t()} | {:error, Response.t()} | {:error, Error.t()}
408
431
def post(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
409
432
do: Request.request(:post, client, url, body, headers, opts)
410
433
 
@@ -415,14 +438,15 @@ defmodule OAuth2.Client do
415
438
An `OAuth2.Error` exception is raised if the request results in an
416
439
error tuple (`{:error, reason}`).
417
440
"""
418
- @spec post!(t, binary, body, headers, Keyword.t) :: Response.t | Error.t
441
+ @spec post!(t, binary, body, headers, Keyword.t()) :: Response.t() | Error.t()
419
442
def post!(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
420
443
do: Request.request!(:post, client, url, body, headers, opts)
421
444
422
445
@doc """
423
446
Makes a `DELETE` request to the given URL using the `OAuth2.AccessToken`.
424
447
"""
425
- @spec delete(t, binary, body, headers, Keyword.t) :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
448
+ @spec delete(t, binary, body, headers, Keyword.t()) ::
449
+ {:ok, Response.t()} | {:error, Response.t()} | {:error, Error.t()}
426
450
def delete(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
427
451
do: Request.request(:delete, client, url, body, headers, opts)
428
452
 
@@ -433,7 +457,7 @@ defmodule OAuth2.Client do
433
457
An `OAuth2.Error` exception is raised if the request results in an
434
458
error tuple (`{:error, reason}`).
435
459
"""
436
- @spec delete!(t, binary, body, headers, Keyword.t) :: Response.t | Error.t
460
+ @spec delete!(t, binary, body, headers, Keyword.t()) :: Response.t() | Error.t()
437
461
def delete!(%Client{} = client, url, body \\ "", headers \\ [], opts \\ []),
438
462
do: Request.request!(:delete, client, url, body, headers, opts)
439
463
 
@@ -454,11 +478,13 @@ defmodule OAuth2.Client do
454
478
|> to_url(:token_url)
455
479
end
456
480
457
- defp token_post_header(%Client{token_method: :post} = client), do:
458
- put_header(client, "content-type", "application/x-www-form-urlencoded")
481
+ defp token_post_header(%Client{token_method: :post} = client),
482
+ do: put_header(client, "content-type", "application/x-www-form-urlencoded")
483
+
459
484
defp token_post_header(%Client{} = client), do: client
460
485
461
486
defp endpoint(client, <<"/"::utf8, _::binary>> = endpoint),
462
487
do: client.site <> endpoint
488
+
463
489
defp endpoint(_client, endpoint), do: endpoint
464
490
end
changed lib/oauth2/error.ex
 
@@ -2,12 +2,12 @@ defmodule OAuth2.Error do
2
2
@moduledoc false
3
3
4
4
@type t :: %__MODULE__{
5
- reason: binary
6
- }
5
+ reason: binary
6
+ }
7
7
8
8
defexception [:reason]
9
9
10
10
def message(%__MODULE__{reason: :econnrefused}), do: "Connection refused"
11
11
def message(%__MODULE__{reason: reason}) when is_binary(reason), do: reason
12
- def message(%__MODULE__{reason: reason}), do: inspect reason
12
+ def message(%__MODULE__{reason: reason}), do: inspect(reason)
13
13
end
changed lib/oauth2/request.ex
 
@@ -11,11 +11,11 @@ defmodule OAuth2.Request do
11
11
@doc """
12
12
Makes a request of given type to the given URL using the `OAuth2.AccessToken`.
13
13
"""
14
- @spec request(atom, Client.t, binary, body, Client.headers, Keyword.t)
15
- :: {:ok, Response.t} | {:error, Response.t} | {:error, Error.t}
14
+ @spec request(atom, Client.t(), binary, body, Client.headers(), Keyword.t()) ::
15
+ {:ok, Response.t()} | {:ok, reference} | {:error, Response.t()} | {:error, Error.t()}
16
16
def request(method, %Client{} = client, url, body, headers, opts) do
17
17
url = client |> process_url(url) |> process_params(opts[:params])
18
- headers = req_headers(client, headers) |> Enum.uniq
18
+ headers = req_headers(client, headers) |> Enum.uniq()
19
19
content_type = content_type(headers)
20
20
serializer = Client.get_serializer(client, content_type)
21
21
body = encode_request_body(body, content_type, serializer)
 
@@ -25,21 +25,24 @@ defmodule OAuth2.Request do
25
25
if Application.get_env(:oauth2, :debug) do
26
26
Logger.debug("""
27
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}
28
+ url: #{inspect(url)}
29
+ method: #{inspect(method)}
30
+ headers: #{inspect(headers)}
31
+ body: #{inspect(body)}
32
+ req_opts: #{inspect(req_opts)}
33
33
""")
34
34
end
35
35
36
36
case :hackney.request(method, url, headers, body, req_opts) do
37
37
{:ok, ref} when is_reference(ref) ->
38
38
{:ok, ref}
39
+
39
40
{:ok, status, headers, ref} when is_reference(ref) ->
40
41
process_body(client, status, headers, ref)
42
+
41
43
{:ok, status, headers, body} when is_binary(body) ->
42
44
process_body(client, status, headers, body)
45
+
43
46
{:error, reason} ->
44
47
{:error, %Error{reason: reason}}
45
48
end
 
@@ -52,22 +55,26 @@ defmodule OAuth2.Request do
52
55
An `OAuth2.Error` exception is raised if the request results in an
53
56
error tuple (`{:error, reason}`).
54
57
"""
55
- @spec request!(atom, Client.t, binary, body, Client.headers, Keyword.t) :: Response.t
58
+ @spec request!(atom, Client.t(), binary, body, Client.headers(), Keyword.t()) :: Response.t()
56
59
def request!(method, %Client{} = client, url, body, headers, opts) do
57
60
case request(method, client, url, body, headers, opts) do
58
61
{:ok, resp} ->
59
62
resp
63
+
60
64
{:error, %Response{status_code: code, headers: headers, body: body}} ->
61
- raise %Error{reason: """
62
- Server responded with status: #{code}
65
+ raise %Error{
66
+ reason: """
67
+ Server responded with status: #{code}
63
68
64
- Headers:
69
+ Headers:
65
70
66
- #{Enum.reduce(headers, "", fn {k, v}, acc -> acc <> "#{k}: #{v}\n" end)}
67
- Body:
71
+ #{Enum.reduce(headers, "", fn {k, v}, acc -> acc <> "#{k}: #{v}\n" end)}
72
+ Body:
73
+
74
+ #{inspect(body)}
75
+ """
76
+ }
68
77
69
- #{inspect body}
70
- """}
71
78
{:error, error} ->
72
79
raise error
73
80
end
 
@@ -85,15 +92,19 @@ defmodule OAuth2.Request do
85
92
case :hackney.body(ref) do
86
93
{:ok, body} ->
87
94
process_body(client, status, headers, body)
95
+
88
96
{:error, reason} ->
89
97
{:error, %Error{reason: reason}}
90
98
end
91
99
end
100
+
92
101
defp process_body(client, status, headers, body) when is_binary(body) do
93
102
resp = Response.new(client, status, headers, body)
103
+
94
104
case status do
95
105
status when status in 200..399 ->
96
106
{:ok, resp}
107
+
97
108
status when status in 400..599 ->
98
109
{:error, resp}
99
110
end
 
@@ -101,11 +112,13 @@ defmodule OAuth2.Request do
101
112
102
113
defp process_params(url, nil),
103
114
do: url
115
+
104
116
defp process_params(url, params),
105
117
do: url <> "?" <> URI.encode_query(params)
106
118
107
119
defp req_headers(%Client{token: nil} = client, headers),
108
120
do: headers ++ client.headers
121
+
109
122
defp req_headers(%Client{token: token} = client, headers),
110
123
do: [authorization_header(token) | headers] ++ client.headers
111
124
 
@@ -116,6 +129,7 @@ defmodule OAuth2.Request do
116
129
case List.keyfind(headers, "accept", 0) do
117
130
{"accept", _} ->
118
131
headers
132
+
119
133
nil ->
120
134
[{"accept", content_type} | headers]
121
135
end
 
@@ -123,11 +137,14 @@ defmodule OAuth2.Request do
123
137
124
138
defp encode_request_body("", _, _), do: ""
125
139
defp encode_request_body([], _, _), do: ""
140
+
126
141
defp encode_request_body(body, "application/x-www-form-urlencoded", _),
127
142
do: URI.encode_query(body)
143
+
128
144
defp encode_request_body(body, _mime, nil) do
129
145
body
130
146
end
147
+
131
148
defp encode_request_body(body, _mime, serializer) do
132
149
serializer.encode!(body)
133
150
end
changed lib/oauth2/response.ex
 
@@ -15,18 +15,19 @@ defmodule OAuth2.Response do
15
15
alias OAuth2.Client
16
16
17
17
@type status_code :: integer
18
- @type headers :: list
19
- @type body :: binary | map
18
+ @type headers :: [{binary, binary}]
19
+ @type body :: binary | map | list
20
20
21
21
@type t :: %__MODULE__{
22
- status_code: status_code,
23
- headers: headers,
24
- body: body
25
- }
22
+ status_code: status_code,
23
+ headers: headers,
24
+ body: body
25
+ }
26
26
27
27
defstruct status_code: nil, headers: [], body: nil
28
28
29
29
@doc false
30
+ @spec new(Client.t(), integer, headers, body) :: t
30
31
def new(client, code, headers, body) do
31
32
headers = process_headers(headers)
32
33
content_type = content_type(headers)
 
@@ -35,7 +36,7 @@ defmodule OAuth2.Response do
35
36
resp = %__MODULE__{status_code: code, headers: headers, body: body}
36
37
37
38
if Application.get_env(:oauth2, :debug) do
38
- Logger.debug("OAuth2 Provider Response #{inspect resp}")
39
+ Logger.debug("OAuth2 Provider Response #{inspect(resp)}")
39
40
end
40
41
41
42
resp
 
@@ -47,9 +48,11 @@ defmodule OAuth2.Response do
47
48
48
49
defp decode_response_body("", _type, _), do: ""
49
50
defp decode_response_body(" ", _type, _), do: ""
51
+
50
52
defp decode_response_body(body, _type, serializer) when serializer != nil do
51
53
serializer.decode!(body)
52
54
end
55
+
53
56
# Facebook sends text/plain tokens!?
54
57
defp decode_response_body(body, "text/plain", _) do
55
58
case URI.decode_query(body) do
 
@@ -57,9 +60,11 @@ defmodule OAuth2.Response do
57
60
_ -> body
58
61
end
59
62
end
63
+
60
64
defp decode_response_body(body, "application/x-www-form-urlencoded", _) do
61
65
URI.decode_query(body)
62
66
end
67
+
63
68
defp decode_response_body(body, _mime, nil) do
64
69
body
65
70
end
changed lib/oauth2/strategy.ex
 
@@ -78,10 +78,10 @@ defmodule OAuth2.Strategy do
78
78
|> merge_params(params)
79
79
end
80
80
"""
81
- @callback authorize_url(Client.t, Client.params) :: Client.t
81
+ @callback authorize_url(Client.t(), Client.params()) :: Client.t()
82
82
83
83
@doc """
84
- Builds the URL to token endpoint.
84
+ Builds the URL to the token endpoint.
85
85
86
86
## Example
87
87
 
@@ -96,7 +96,7 @@ defmodule OAuth2.Strategy do
96
96
|> put_headers(headers)
97
97
end
98
98
"""
99
- @callback get_token(Client.t, Client.params, Client.headers) :: Client.t
99
+ @callback get_token(Client.t(), Client.params(), Client.headers()) :: Client.t()
100
100
101
101
defmacro __using__(_) do
102
102
quote do
changed lib/oauth2/strategy/auth_code.ex
 
@@ -30,6 +30,7 @@ defmodule OAuth2.Strategy.AuthCode do
30
30
The authorization URL endpoint of the provider.
31
31
params additional query parameters for the URL
32
32
"""
33
+ @impl true
33
34
def authorize_url(client, params) do
34
35
client
35
36
|> put_param(:response_type, "code")
 
@@ -41,11 +42,12 @@ defmodule OAuth2.Strategy.AuthCode do
41
42
@doc """
42
43
Retrieve an access token given the specified validation code.
43
44
"""
45
+ @impl true
44
46
def get_token(client, params, headers) do
45
47
{code, params} = Keyword.pop(params, :code, client.params["code"])
46
48
47
49
unless code do
48
- raise OAuth2.Error, reason: "Missing required key `code` for `#{inspect __MODULE__}`"
50
+ raise OAuth2.Error, reason: "Missing required key `code` for `#{inspect(__MODULE__)}`"
49
51
end
50
52
51
53
client
 
@@ -54,7 +56,7 @@ defmodule OAuth2.Strategy.AuthCode do
54
56
|> put_param(:client_id, client.client_id)
55
57
|> put_param(:redirect_uri, client.redirect_uri)
56
58
|> merge_params(params)
59
+ |> basic_auth()
57
60
|> put_headers(headers)
58
61
end
59
62
end
60
-
changed lib/oauth2/strategy/client_credentials.ex
 
@@ -20,6 +20,7 @@ defmodule OAuth2.Strategy.ClientCredentials do
20
20
@doc """
21
21
Not used for this strategy.
22
22
"""
23
+ @impl true
23
24
def authorize_url(_client, _params) do
24
25
raise OAuth2.Error, reason: "This strategy does not implement `authorize_url`."
25
26
end
 
@@ -27,6 +28,7 @@ defmodule OAuth2.Strategy.ClientCredentials do
27
28
@doc """
28
29
Retrieve an access token given the specified strategy.
29
30
"""
31
+ @impl true
30
32
def get_token(client, params, headers) do
31
33
{auth_scheme, params} = Keyword.pop(params, :auth_scheme, "auth_header")
32
34
 
@@ -37,17 +39,12 @@ defmodule OAuth2.Strategy.ClientCredentials do
37
39
|> put_headers(headers)
38
40
end
39
41
40
- defp auth_scheme(client, "auth_header"), do: auth_header(client)
42
+ defp auth_scheme(client, "auth_header"), do: basic_auth(client)
41
43
defp auth_scheme(client, "request_body"), do: request_body(client)
42
44
43
- defp auth_header(%{client_id: id, client_secret: secret} = client) do
44
- put_header(client, "Authorization", "Basic " <> Base.encode64(id <> ":" <> secret))
45
- end
46
-
47
45
defp request_body(client) do
48
46
client
49
47
|> put_param(:client_id, client.client_id)
50
48
|> put_param(:client_secret, client.client_secret)
51
49
end
52
50
end
53
-
changed lib/oauth2/strategy/password.ex
 
@@ -25,6 +25,7 @@ defmodule OAuth2.Strategy.Password do
25
25
@doc """
26
26
Not used for this strategy.
27
27
"""
28
+ @impl true
28
29
def authorize_url(_client, _params) do
29
30
raise OAuth2.Error, reason: "This strategy does not implement `authorize_url`."
30
31
end
 
@@ -32,21 +33,22 @@ defmodule OAuth2.Strategy.Password do
32
33
@doc """
33
34
Retrieve an access token given the specified End User username and password.
34
35
"""
36
+ @impl true
35
37
def get_token(client, params, headers) do
36
38
{username, params} = Keyword.pop(params, :username, client.params["username"])
37
39
{password, params} = Keyword.pop(params, :password, client.params["password"])
38
40
39
41
unless username && password do
40
- raise OAuth2.Error, reason: "Missing required keys `username` and `password` for #{inspect __MODULE__}"
42
+ raise OAuth2.Error,
43
+ reason: "Missing required keys `username` and `password` for #{inspect(__MODULE__)}"
41
44
end
42
45
43
46
client
44
47
|> put_param(:username, username)
45
48
|> put_param(:password, password)
46
49
|> put_param(:grant_type, "password")
47
- |> put_param(:client_id, client.client_id)
48
- |> put_param(:client_secret, client.client_secret)
49
50
|> merge_params(params)
51
+ |> basic_auth()
50
52
|> put_headers(headers)
51
53
end
52
54
end
changed lib/oauth2/strategy/refresh.ex
 
@@ -27,6 +27,7 @@ defmodule OAuth2.Strategy.Refresh do
27
27
@doc """
28
28
Not used for this strategy.
29
29
"""
30
+ @impl true
30
31
def authorize_url(_client, _params) do
31
32
raise OAuth2.Error, reason: "This strategy does not implement `authorize_url`."
32
33
end
 
@@ -34,19 +35,20 @@ defmodule OAuth2.Strategy.Refresh do
34
35
@doc """
35
36
Refresh an access token given the specified validation code.
36
37
"""
38
+ @impl true
37
39
def get_token(client, params, headers) do
38
40
{token, params} = Keyword.pop(params, :refresh_token, client.params["refresh_token"])
39
41
40
42
unless token do
41
- raise OAuth2.Error, reason: "Missing required key `refresh_token` for `#{inspect __MODULE__}`"
43
+ raise OAuth2.Error,
44
+ reason: "Missing required key `refresh_token` for `#{inspect(__MODULE__)}`"
42
45
end
43
46
44
47
client
45
48
|> put_param(:refresh_token, token)
46
49
|> put_param(:grant_type, "refresh_token")
47
- |> put_param(:client_id, client.client_id)
48
- |> put_param(:client_secret, client.client_secret)
49
50
|> merge_params(params)
51
+ |> basic_auth()
50
52
|> put_headers(headers)
51
53
end
52
54
end
changed lib/oauth2/util.ex
 
@@ -1,17 +1,20 @@
1
1
defmodule OAuth2.Util do
2
2
@moduledoc false
3
3
4
+ @spec unix_now :: integer
4
5
def unix_now do
5
- {mega, sec, _micro} = :os.timestamp
6
- (mega * 1_000_000) + sec
6
+ {mega, sec, _micro} = :os.timestamp()
7
+ mega * 1_000_000 + sec
7
8
end
8
9
10
+ @spec content_type([{binary, binary}]) :: binary
9
11
def content_type(headers) do
10
12
case get_content_type(headers) do
11
13
{_, content_type} ->
12
14
content_type
13
15
|> remove_params
14
16
|> parse_content_type
17
+
15
18
nil ->
16
19
"application/json"
17
20
end
 
@@ -26,8 +29,9 @@ defmodule OAuth2.Util do
26
29
case String.split(content_type, "/") do
27
30
[type, subtype] ->
28
31
type <> "/" <> subtype
29
- [bad_type] ->
30
- raise OAuth2.Error, reason: "bad content-type: #{bad_type}"
32
+
33
+ _ ->
34
+ raise OAuth2.Error, reason: "bad content-type: #{content_type}"
31
35
end
32
36
end
changed mix.exs
 
@@ -1,24 +1,27 @@
1
1
defmodule OAuth2.Mixfile do
2
2
use Mix.Project
3
3
4
- @version "1.0.1"
4
+ @version "2.0.0"
5
5
6
6
def project do
7
- [app: :oauth2,
8
- name: "OAuth2",
9
- version: @version,
10
- elixir: "~> 1.2",
11
- deps: deps(),
12
- package: package(),
13
- description: description(),
14
- docs: docs(),
15
- elixirc_paths: elixirc_paths(Mix.env),
16
- test_coverage: [tool: ExCoveralls],
17
- preferred_cli_env: [
18
- coveralls: :test,
19
- "coveralls.detail": :test,
20
- docs: :dev
21
- ]]
7
+ [
8
+ app: :oauth2,
9
+ name: "OAuth2",
10
+ version: @version,
11
+ elixir: "~> 1.2",
12
+ deps: deps(),
13
+ package: package(),
14
+ description: description(),
15
+ docs: docs(),
16
+ elixirc_paths: elixirc_paths(Mix.env()),
17
+ test_coverage: [tool: ExCoveralls],
18
+ preferred_cli_env: [
19
+ coveralls: :test,
20
+ "coveralls.detail": :test,
21
+ "coveralls.html": :test,
22
+ docs: :dev
23
+ ]
24
+ ]
22
25
end
23
26
24
27
def application do
 
@@ -26,17 +29,20 @@ defmodule OAuth2.Mixfile do
26
29
end
27
30
28
31
defp deps do
29
- [{:hackney, "~> 1.13"},
32
+ [
33
+ {:hackney, "~> 1.13"},
30
34
31
- # Test dependencies
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
- {:dialyxir, "~> 0.5", only: [:dev], runtime: false},
35
+ # Test dependencies
36
+ {:jason, "~> 1.0", only: [:dev, :test]},
37
+ {:bypass, "~> 0.9", only: :test},
38
+ {:plug_cowboy, "~> 1.0", only: :test},
39
+ {:excoveralls, "~> 0.9", only: :test},
40
+ {:credo, "~> 1.1.0", only: [:dev, :test], runtime: false},
41
+ {:dialyxir, "~> 1.0.0-rc.6", only: [:dev], runtime: false},
37
42
38
- # Docs dependencies
39
- {:ex_doc, "~> 0.19", only: :dev}]
43
+ # Docs dependencies
44
+ {:ex_doc, "~> 0.19", only: :dev}
45
+ ]
40
46
end
41
47
42
48
defp description do
 
@@ -44,17 +50,21 @@ defmodule OAuth2.Mixfile do
44
50
end
45
51
46
52
defp docs do
47
- [extras: ["README.md"],
48
- main: "readme",
49
- source_ref: "v#{@version}",
50
- source_url: "https://github.com/scrogson/oauth2"]
53
+ [
54
+ extras: ["README.md"],
55
+ main: "readme",
56
+ source_ref: "v#{@version}",
57
+ source_url: "https://github.com/scrogson/oauth2"
58
+ ]
51
59
end
52
60
53
61
defp package do
54
- [files: ["lib", "mix.exs", "README.md", "LICENSE"],
55
- maintainers: ["Sonny Scroggin"],
56
- licenses: ["MIT"],
57
- links: %{github: "https://github.com/scrogson/oauth2"}]
62
+ [
63
+ files: ["lib", "mix.exs", "README.md", "LICENSE"],
64
+ maintainers: ["Sonny Scroggin"],
65
+ licenses: ["MIT"],
66
+ links: %{github: "https://github.com/scrogson/oauth2"}
67
+ ]
58
68
end
59
69
60
70
defp elixirc_paths(:test), do: ["lib", "test/support"]