Skip to content

Commit 7607463

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

File tree

8 files changed

+291
-185
lines changed

8 files changed

+291
-185
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 wait until `:before_send` callback before updating cookie and backend store
28+
* [`Pow.Plug.Base`] Now registers `:before_send` callbacks
29+
* [`Pow.Plug.Session`] Now wait until `:before_send` callback before updating plug session and backend store
2630

2731
### Removed
2832

lib/extensions/persistent_session/plug/base.ex

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ defmodule PowPersistentSession.Plug.Base do
2424
alias Pow.{Config, Plug, Store.Backend.EtsCache}
2525
alias PowPersistentSession.Store.PersistentSessionCache
2626

27+
@callback init(Config.t()) :: Config.t()
28+
@callback call(Conn.t(), Config.t()) :: Conn.t()
2729
@callback authenticate(Conn.t(), Config.t()) :: Conn.t()
2830
@callback create(Conn.t(), map(), Config.t()) :: Conn.t()
2931

@@ -32,12 +34,14 @@ defmodule PowPersistentSession.Plug.Base do
3234
quote do
3335
@behaviour unquote(__MODULE__)
3436

37+
@before_send_private_key String.to_atom(Macro.underscore(__MODULE__) <> "/before_send")
38+
3539
import unquote(__MODULE__)
3640

37-
@spec init(Config.t()) :: Config.t()
41+
@impl true
3842
def init(config), do: config
3943

40-
@spec call(Conn.t(), Config.t()) :: Conn.t()
44+
@impl true
4145
def call(conn, config) do
4246
config =
4347
conn
@@ -47,6 +51,17 @@ defmodule PowPersistentSession.Plug.Base do
4751
conn
4852
|> Conn.put_private(:pow_persistent_session, {__MODULE__, config})
4953
|> authenticate(config)
54+
|> Conn.register_before_send(fn conn ->
55+
conn.private
56+
|> Map.get(@before_send_private_key, [])
57+
|> Enum.reduce(conn, & &1.(&2))
58+
end)
59+
end
60+
61+
defp register_before_send(conn, callback) do
62+
callbacks = Map.get(conn.private, @before_send_private_key, []) ++ [callback]
63+
64+
Conn.put_private(conn, @before_send_private_key, callbacks)
5065
end
5166
end
5267
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: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ defmodule Pow.Plug.Base do
4343
quote do
4444
@behaviour unquote(__MODULE__)
4545

46+
@before_send_private_key String.to_atom(Macro.underscore(__MODULE__) <> "/before_send")
47+
4648
@doc false
49+
@impl true
4750
def init(config), do: config
4851

4952
@doc """
@@ -57,13 +60,25 @@ defmodule Pow.Plug.Base do
5760
If a user can't be fetched with `Pow.Plug.current_user/2`, `do_fetch/2`
5861
will be called.
5962
"""
63+
@impl true
6064
def call(conn, config) do
6165
config = put_plug(config)
6266
conn = Plug.put_config(conn, config)
6367

6468
conn
6569
|> Plug.current_user(config)
6670
|> maybe_fetch_user(conn, config)
71+
|> Conn.register_before_send(fn conn ->
72+
conn.private
73+
|> Map.get(@before_send_private_key, [])
74+
|> Enum.reduce(conn, & &1.(&2))
75+
end)
76+
end
77+
78+
defp register_before_send(conn, callback) do
79+
callbacks = Map.get(conn.private, @before_send_private_key, []) ++ [callback]
80+
81+
Conn.put_private(conn, @before_send_private_key, callbacks)
6782
end
6883

6984
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)