Skip to content

Commit cd45572

Browse files
authored
Merge pull request #189 from pow-auth/fix-docs
Correct examples in OIDC and OAuth module docs
2 parents cda76a6 + 78b46f4 commit cd45572

File tree

7 files changed

+241
-142
lines changed

7 files changed

+241
-142
lines changed

README.md

Lines changed: 66 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -39,77 +39,31 @@ Multi-provider authentication framework.
3939
* VK - `Assent.Strategy.VK`
4040
* Zitadel - `Assent.Strategy.Zitadel`
4141

42-
<!-- MDOC !-->
43-
44-
## Installation
45-
46-
Add Assent to your list of dependencies in `mix.exs`:
47-
48-
```elixir
49-
defp deps do
50-
[
51-
# ...
52-
{:assent, "~> 0.3.0"}
53-
]
54-
end
55-
```
42+
## Usage
5643

57-
Run `mix deps.get` to install it.
58-
59-
#### HTTP client installation
44+
A strategy consists of two phases; request and callback. In the request phase, the user will be redirected to the auth provider for authentication and then returned to initiate the callback phase.
6045

61-
By default, `Req` is used if you have it in your dependency list. If not, Erlang's `:httpc` will be used instead.
46+
### Single provider
6247

63-
If you are using `:httpc` you should add the following dependencies to enable SSL validation:
48+
This is an example using the `Assent.Strategy.Github` strategy with `Plug`:
6449

6550
```elixir
66-
defp deps do
67-
[
68-
# ...
69-
# Required for SSL validation when using the `:httpc` adapter
70-
{:certifi, "~> 2.4"},
71-
{:ssl_verify_fun, "~> 1.1"}
72-
]
73-
end
74-
```
75-
76-
You must also add `:inets` to `:extra_applications` in `mix.exs`:
77-
78-
```elixir
79-
def application do
80-
[
81-
# ...
82-
extra_applications: [
83-
# ...
84-
:inets
85-
]
86-
]
87-
end
88-
```
89-
90-
This is not necessary if you use another HTTP adapter like `Req` or `Finch`.
91-
92-
## Getting started
93-
94-
A strategy consists of two phases; request and callback. In the request phase, the user would normally be redirected to the provider for authentication and then returned to initiate the callback phase.
95-
96-
### Single provider example
97-
98-
```elixir
99-
defmodule ProviderAuth do
51+
defmodule GithubAuth do
10052
import Plug.Conn
10153

102-
alias Assent.{Config, Strategy.Github}
54+
alias Assent.Strategy.Github
10355

104-
@config [
105-
client_id: "REPLACE_WITH_CLIENT_ID",
106-
client_secret: "REPLACE_WITH_CLIENT_SECRET",
107-
redirect_uri: "http://localhost:4000/auth/github/callback"
108-
]
56+
defp config do
57+
[
58+
client_id: Application.fetch_env!(:my_app, :github, :client_id),
59+
client_secret: Application.fetch_env!(:my_app, :github, :client_secret),
60+
redirect_uri: "http://localhost:4000/auth/github/callback"
61+
]
62+
end
10963

11064
# http://localhost:4000/auth/github
11165
def request(conn) do
112-
@config
66+
config()
11367
|> Github.authorize_url()
11468
|> case do
11569
{:ok, %{url: url, session_params: session_params}} ->
@@ -122,8 +76,9 @@ defmodule ProviderAuth do
12276
|> put_resp_header("location", url)
12377
|> send_resp(302, "")
12478

125-
{:error, error} ->
79+
{:error, _error} ->
12680
# Something went wrong generating the request authorization url
81+
send_resp(conn, 500, "Failed authorization")
12782
end
12883
end
12984

@@ -139,24 +94,32 @@ defmodule ProviderAuth do
13994
# request phase will be used in the callback phase
14095
session_params = get_session(conn, :session_params)
14196

142-
@config
97+
conn = delete_session(conn, :session_params)
98+
99+
config()
143100
# Session params should be added to the config so the strategy can use them
144101
|> Keyword.put(:session_params, session_params)
145102
|> Github.callback(params)
146103
|> case do
147104
{:ok, %{user: user, token: token}} ->
148105
# Authorization succesful
106+
conn
107+
|> put_session(:user, user)
108+
|> put_session(:token, token)
109+
|> put_resp_header("location", "/")
110+
|> send_resp(302, "")
149111

150-
{:error, error} ->
112+
{:error, _error} ->
151113
# Authorizaiton failed
114+
send_resp(conn, 500, "Failed authorization")
152115
end
153116
end
154117
end
155118
```
156119

157-
### Multi-provider example
120+
### Multi-provider
158121

159-
This is a generalized flow that's similar to what's used in [PowAssent](https://github.com/danschultzer/pow_assent).
122+
All assent strategies work the same way, so if you have more than one strategy you may want to set up a single module to handle any of the auth strategies. This example is a generalized flow that's similar to what's used in `PowAssent`.
160123

161124
```elixir
162125
config :my_app, :strategies,
@@ -165,13 +128,13 @@ config :my_app, :strategies,
165128
client_secret: "REPLACE_WITH_CLIENT_SECRET",
166129
strategy: Assent.Strategy.Github
167130
],
168-
# ...
131+
# Other strategies
169132
```
170133

171134
```elixir
172135
defmodule MultiProviderAuth do
173-
@spec request(atom()) :: {:ok, map()} | {:error, term()}
174-
def request(provider) do
136+
@spec authorize_url(atom()) :: {:ok, map()} | {:error, term()}
137+
def authorize_url(provider) do
175138
config = config!(provider)
176139

177140
config[:strategy].authorize_url()
@@ -196,7 +159,7 @@ defmodule MultiProviderAuth do
196159
end
197160
```
198161

199-
## Custom provider
162+
### Custom provider
200163

201164
You can create custom strategies. Here's an example of an OAuth 2.0 implementation using `Assent.Strategy.OAuth2.Base`:
202165

@@ -247,21 +210,29 @@ If you need more control over the strategy than what the macros give you, you ca
247210
defmodule TestProvider do
248211
@behaviour Assent.Strategy
249212

250-
@spec authorize_url(Keyword.t()) :: {:ok, %{url: binary()}} | {:error, term()}
213+
alias Assent.Strategy, as: Helpers
214+
215+
@impl Assent.Strategy
251216
def authorize_url(config) do
252-
# Generate authorization url
217+
# Generate redirect URL
218+
219+
{:ok, %{url: url}}
253220
end
254221

255-
@spec callback(Keyword.t(), map()) :: {:ok, %{user: map(), token: map()}} | {:error, term()}
222+
@impl Assent.Strategy
256223
def callback(config, params) do
257-
# Handle callback response
224+
# Fetch user data
225+
226+
user = Helpers.normalize_userinfo(userinfo)
227+
228+
{:ok, %{user: user}}
258229
end
259230
end
260231
```
261232

262233
## HTTP Client
263234

264-
Assent supports [`Req`](https://github.com/wojtekmach/req), [`Finch`](https://github.com/sneako/finch), and [`:httpc`](https://www.erlang.org/doc/man/httpc.html) out of the box. The `Req` HTTP client adapter will be used by default if enabled, otherwise Erlang's `:httpc` adapter will be included.
235+
Assent supports [`Req`](https://github.com/wojtekmach/req), [`Finch`](https://github.com/sneako/finch), and [`:httpc`](https://www.erlang.org/doc/man/httpc.html) out of the box. The `Req` HTTP client adapter will be used by default if enabled, otherwise Erlang's `:httpc` adapter will be used.
265236

266237
You can explicitly set the HTTP client adapter in the configuration:
267238

@@ -279,9 +250,9 @@ Or globally in the config:
279250
config :assent, http_adapter: Assent.HTTPAdapter.Httpc
280251
```
281252

282-
### `Req`
253+
### Req
283254

284-
Req doesn't require any additional configuration and will work out of the box:
255+
`Req` doesn't require any additional configuration and will work out of the box:
285256

286257
```elixir
287258
defp deps do
@@ -292,7 +263,7 @@ defp deps do
292263
end
293264
```
294265

295-
### `:httpc`
266+
### :httpc
296267

297268
If `Req` is not available, Erlangs built-in `:httpc` is used for requests. SSL verification is automatically enabled when `:certifi` and `:ssl_verify_fun` packages are available. `:httpc` only supports HTTP/1.1.
298269

@@ -311,7 +282,7 @@ You must include `:inets` to `:extra_applications` to include `:httpc` in your r
311282

312283
### Finch
313284

314-
Finch will require a supervisor in your application.
285+
`Finch` will require a supervisor in your application.
315286

316287
Update `mix.exs`:
317288

@@ -365,6 +336,23 @@ Or globally in the config:
365336
config :assent, jwt_adapter: Assent.JWTAdapter.JOSE
366337
```
367338

339+
<!-- MDOC !-->
340+
341+
## Installation
342+
343+
Add Assent to your list of dependencies in `mix.exs`:
344+
345+
```elixir
346+
defp deps do
347+
[
348+
# ...
349+
{:assent, "~> 0.3.0"}
350+
]
351+
end
352+
```
353+
354+
Run `mix deps.get` to install it.
355+
368356
## LICENSE
369357

370358
(The MIT License)

lib/assent/strategies/apple.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ defmodule Assent.Strategy.Apple do
4141
{:ok, %{session_params: session_params}} = Assent.Strategy.Apple.authorize_url(config)
4242
4343
Use the `session_params[:state]` value for `[STATE]`. The callback phase
44-
would be identical to how it's explained in the [README](README.md).
44+
would be identical to how it's explained in the `Assent` docs.
4545
4646
See https://developer.apple.com/documentation/signinwithapplejs/configuring_your_webpage_for_sign_in_with_apple
4747
for more.

lib/assent/strategies/oauth.ex

Lines changed: 54 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -23,25 +23,60 @@ defmodule Assent.Strategy.OAuth do
2323
`:private_key_path`, required if `:signature_method` is `:rsa_sha1` and
2424
`:private_key_path` hasn't been set
2525
26-
## Usage
27-
28-
config = [
29-
consumer_key: "REPLACE_WITH_CONSUMER_KEY",
30-
consumer_secret: "REPLACE_WITH_CONSUMER_SECRET",
31-
base_url: "https://auth.example.com",
32-
authorization_params: [scope: "user:read user:write"],
33-
user_url: "https://example.com/api/user"
34-
]
35-
36-
{:ok, {url: url, session_params: session_params}} =
37-
config
38-
|> Keyword.put(:redirect_uri, "http://localhost:4000/auth/callback")
39-
|> OAuth.authorize_url()
40-
41-
{:ok, %{user: user, token: token}} =
42-
config
43-
|> Keyword.put(:session_params, session_params)
44-
|> OAuth.callback(params)
26+
## Examples
27+
28+
defmodule OAuth do
29+
import Plug.Conn
30+
31+
alias Assent.Strategy.OAuth
32+
33+
defp config do
34+
[
35+
consumer_key: Application.fetch_env!(:my_app, :oauth, :consumer_key),
36+
consumer_secret: Application.fetch_env!(:my_app, :oauth, :consumer_secret),
37+
base_url: "https://auth.example.com",
38+
authorization_params: [scope: "user:read user:write"],
39+
user_url: "https://example.com/api/user",
40+
redirect_uri: "http://localhost:4000/auth/callback"
41+
]
42+
end
43+
44+
def request(conn) do
45+
config()
46+
|> OAuth.authorize_url()
47+
|> case do
48+
{:ok, %{url: url, session_params: session_params}} ->
49+
conn
50+
|> put_session(:session_params, session_params)
51+
|> put_resp_header("location", url)
52+
|> send_resp(302, "")
53+
54+
{:error, _error} ->
55+
send_resp(conn, 500, "Failed authorization")
56+
end
57+
end
58+
59+
def callback(conn) do
60+
%{params: params} = fetch_query_params(conn)
61+
session_params = get_session(conn, :session_params)
62+
conn = delete_session(conn, :session_params)
63+
64+
config()
65+
|> Keyword.put(:session_params, session_params)
66+
|> OAuth.callback(params)
67+
|> case do
68+
{:ok, %{user: user, token: token}} ->
69+
conn
70+
|> put_session(:user, user)
71+
|> put_session(:token, token)
72+
|> put_resp_header("location", "/")
73+
|> send_resp(302, "")
74+
75+
{:error, _error} ->
76+
send_resp(conn, 500, "Failed authorization")
77+
end
78+
end
79+
end
4580
"""
4681
@behaviour Assent.Strategy
4782

0 commit comments

Comments
 (0)