Skip to content

Commit 0170dd2

Browse files
committed
Use :before_send callback with plugs for sessions
1 parent 9932d7f commit 0170dd2

File tree

8 files changed

+301
-187
lines changed

8 files changed

+301
-187
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
* [`PowPersistentSession.Plug.Cookie`] Removed renewal of cookie as the token will always expire
2424
* [`PowPersistentSession.Plug.Cookie`] No longer expires invalid cookies
2525
* [`Pow.Operations`] Added `Pow.Operations.fetch_primary_key_values/2`
26+
* [`PowPersistentSession.Plug.Base`] Now registers `:before_send` callbacks
27+
* [`PowPersistentSession.Plug.Cookie`] Now updates cookie and backend store in `:before_send` callback
28+
* [`Pow.Plug.Base`] Now registers `:before_send` callbacks
29+
* [`Pow.Plug.Session`] Now updates plug session and backend store in `:before_send` callback
2630

2731
### Removed
2832

lib/extensions/persistent_session/plug/base.ex

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ defmodule PowPersistentSession.Plug.Base do
22
@moduledoc """
33
Base module for setting up persistent session plugs.
44
5+
Any writes to backend store or client should occur in `:before_send` callback
6+
as defined in `Plug.Conn`. To ensure that the callbacks are called in the
7+
order they were set, a `register_before_send/2` method is used to set
8+
callbacks instead of `Plug.Conn.register_before_send/2`.
9+
510
See `PowPersistentSession.Plug.Cookie` for an implementation example.
611
712
## Configuration options
@@ -24,6 +29,8 @@ defmodule PowPersistentSession.Plug.Base do
2429
alias Pow.{Config, Plug, Store.Backend.EtsCache}
2530
alias PowPersistentSession.Store.PersistentSessionCache
2631

32+
@callback init(Config.t()) :: Config.t()
33+
@callback call(Conn.t(), Config.t()) :: Conn.t()
2734
@callback authenticate(Conn.t(), Config.t()) :: Conn.t()
2835
@callback create(Conn.t(), map(), Config.t()) :: Conn.t()
2936

@@ -32,12 +39,14 @@ defmodule PowPersistentSession.Plug.Base do
3239
quote do
3340
@behaviour unquote(__MODULE__)
3441

42+
@before_send_private_key String.to_atom(Macro.underscore(__MODULE__) <> "/before_send")
43+
3544
import unquote(__MODULE__)
3645

37-
@spec init(Config.t()) :: Config.t()
46+
@impl true
3847
def init(config), do: config
3948

40-
@spec call(Conn.t(), Config.t()) :: Conn.t()
49+
@impl true
4150
def call(conn, config) do
4251
config =
4352
conn
@@ -47,6 +56,17 @@ defmodule PowPersistentSession.Plug.Base do
4756
conn
4857
|> Conn.put_private(:pow_persistent_session, {__MODULE__, config})
4958
|> authenticate(config)
59+
|> Conn.register_before_send(fn conn ->
60+
conn.private
61+
|> Map.get(@before_send_private_key, [])
62+
|> Enum.reduce(conn, & &1.(&2))
63+
end)
64+
end
65+
66+
defp register_before_send(conn, callback) do
67+
callbacks = Map.get(conn.private, @before_send_private_key, []) ++ [callback]
68+
69+
Conn.put_private(conn, @before_send_private_key, callbacks)
5070
end
5171
end
5272
end

lib/extensions/persistent_session/plug/cookie.ex

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -89,17 +89,23 @@ defmodule PowPersistentSession.Plug.Cookie do
8989
"""
9090
@spec create(Conn.t(), map(), Config.t()) :: Conn.t()
9191
def create(conn, user, config) do
92+
conn
93+
|> delete(config)
94+
|> before_send_create(user, config)
95+
end
96+
97+
defp before_send_create(conn, user, config) do
9298
{store, store_config} = store(config)
9399
cookie_key = cookie_key(config)
94100
key = cookie_id(config)
95101
value = persistent_session_value(conn, user, config)
96102
opts = cookie_opts(config)
97103

98-
store.put(store_config, key, value)
104+
register_before_send(conn, fn conn ->
105+
store.put(store_config, key, value)
99106

100-
conn
101-
|> delete(config)
102-
|> Conn.put_resp_cookie(cookie_key, key, opts)
107+
Conn.put_resp_cookie(conn, cookie_key, key, opts)
108+
end)
103109
end
104110

105111
defp persistent_session_value(conn, user, config) do
@@ -150,15 +156,21 @@ defmodule PowPersistentSession.Plug.Cookie do
150156
cookie_key = cookie_key(config)
151157

152158
case conn.req_cookies[cookie_key] do
153-
nil ->
154-
conn
155-
156-
key_id ->
157-
expire_token_in_store(key_id, config)
158-
delete_cookie(conn, cookie_key, config)
159+
nil -> conn
160+
key -> before_send_delete(conn, key, config)
159161
end
160162
end
161163

164+
defp before_send_delete(conn, key, config) do
165+
cookie_key = cookie_key(config)
166+
167+
register_before_send(conn, fn conn ->
168+
expire_token_in_store(key, config)
169+
170+
delete_cookie(conn, cookie_key, config)
171+
end)
172+
end
173+
162174
defp expire_token_in_store(key_id, config) do
163175
{store, store_config} = store(config)
164176

lib/pow/plug/base.ex

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ defmodule Pow.Plug.Base do
44
assign a user in the connection if it has not already been assigned. The user
55
will be assigned automatically in any of the operations.
66
7+
Any writes to backend store or client should occur in `:before_send` callback
8+
as defined in `Plug.Conn`. To ensure that the callbacks are called in the
9+
order they were set, a `register_before_send/2` method is used to set
10+
callbacks instead of `Plug.Conn.register_before_send/2`.
11+
712
## Example
813
914
defmodule MyAppWeb.Pow.CustomPlug do
@@ -43,7 +48,10 @@ defmodule Pow.Plug.Base do
4348
quote do
4449
@behaviour unquote(__MODULE__)
4550

51+
@before_send_private_key String.to_atom(Macro.underscore(__MODULE__) <> "/before_send")
52+
4653
@doc false
54+
@impl true
4755
def init(config), do: config
4856

4957
@doc """
@@ -57,13 +65,25 @@ defmodule Pow.Plug.Base do
5765
If a user can't be fetched with `Pow.Plug.current_user/2`, `do_fetch/2`
5866
will be called.
5967
"""
68+
@impl true
6069
def call(conn, config) do
6170
config = put_plug(config)
6271
conn = Plug.put_config(conn, config)
6372

6473
conn
6574
|> Plug.current_user(config)
6675
|> maybe_fetch_user(conn, config)
76+
|> Conn.register_before_send(fn conn ->
77+
conn.private
78+
|> Map.get(@before_send_private_key, [])
79+
|> Enum.reduce(conn, & &1.(&2))
80+
end)
81+
end
82+
83+
defp register_before_send(conn, callback) do
84+
callbacks = Map.get(conn.private, @before_send_private_key, []) ++ [callback]
85+
86+
Conn.put_private(conn, @before_send_private_key, callbacks)
6787
end
6888

6989
defp put_plug(config) do

lib/pow/plug/session.ex

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ defmodule Pow.Plug.Session do
130130
@spec fetch(Conn.t(), Config.t()) :: {Conn.t(), map() | nil}
131131
def fetch(conn, config) do
132132
{store, store_config} = store(config)
133-
key = client_store_fetch(conn, config)
133+
{key, conn} = client_store_fetch(conn, config)
134134

135135
{key, store.get(store_config, key)}
136136
|> convert_old_session_value()
@@ -158,18 +158,14 @@ defmodule Pow.Plug.Session do
158158
@impl true
159159
@spec create(Conn.t(), map(), Config.t()) :: {Conn.t(), map()}
160160
def create(conn, user, config) do
161-
{store, store_config} = store(config)
162-
metadata = Map.get(conn.private, :pow_session_metadata, [])
163-
{user, metadata} = session_value(user, metadata)
164-
key = session_id(config)
165-
166-
store.put(store_config, key, {user, metadata})
161+
metadata = Map.get(conn.private, :pow_session_metadata, [])
162+
{user, metadata} = session_value(user, metadata)
167163

168164
conn =
169165
conn
170166
|> delete(config)
167+
|> before_send_create({user, metadata}, config)
171168
|> Conn.put_private(:pow_session_metadata, metadata)
172-
|> client_store_put(key, config)
173169

174170
{conn, user}
175171
end
@@ -183,6 +179,17 @@ defmodule Pow.Plug.Session do
183179
{user, metadata}
184180
end
185181

182+
defp before_send_create(conn, value, config) do
183+
{store, store_config} = store(config)
184+
key = session_id(config)
185+
186+
register_before_send(conn, fn conn ->
187+
store.put(store_config, key, value)
188+
189+
client_store_put(conn, key, config)
190+
end)
191+
end
192+
186193
@doc """
187194
Delete an existing session in the credentials cache.
188195
@@ -196,16 +203,19 @@ defmodule Pow.Plug.Session do
196203
@spec delete(Conn.t(), Config.t()) :: Conn.t()
197204
def delete(conn, config) do
198205
case client_store_fetch(conn, config) do
199-
nil ->
200-
conn
206+
{nil, conn} -> conn
207+
{key, conn} -> before_send_delete(conn, key, config)
208+
end
209+
end
201210

202-
key ->
203-
{store, store_config} = store(config)
211+
defp before_send_delete(conn, key, config) do
212+
{store, store_config} = store(config)
204213

205-
store.delete(store_config, key)
214+
register_before_send(conn, fn conn ->
215+
store.delete(store_config, key)
206216

207-
client_store_delete(conn, config)
208-
end
217+
client_store_delete(conn, config)
218+
end)
209219
end
210220

211221
# TODO: Remove by 1.1.0
@@ -268,9 +278,10 @@ defmodule Pow.Plug.Session do
268278
defp timestamp, do: :os.system_time(:millisecond)
269279

270280
defp client_store_fetch(conn, config) do
271-
conn
272-
|> Conn.fetch_session()
273-
|> Conn.get_session(session_key(config))
281+
conn = Conn.fetch_session(conn)
282+
key = Conn.get_session(conn, session_key(config))
283+
284+
{key, conn}
274285
end
275286

276287
defp client_store_put(conn, value, config) do

0 commit comments

Comments
 (0)