@@ -3,8 +3,8 @@ defmodule PowPersistentSession.Plug.Cookie do
3
3
This plug will handle persistent user sessions with cookies.
4
4
5
5
By default, the cookie will expire after 30 days. The cookie expiration will
6
- be renewed on every request. The token in the cookie can only be used once to
7
- create a session.
6
+ be renewed on every request where a user is assigned to the conn. The token
7
+ in the cookie can only be used once to create a session.
8
8
9
9
If an assigned private `:pow_session_metadata` key exists in the conn with a
10
10
keyword list containing a `:fingerprint` key, that fingerprint value will be
@@ -36,11 +36,15 @@ defmodule PowPersistentSession.Plug.Cookie do
36
36
* `:persistent_session_ttl` - used for both backend store and max age for
37
37
cookie. See `PowPersistentSession.Plug.Base` for more.
38
38
39
- * `:persistent_session_cookie_opts` - a keyword list of cookie options, see
39
+ * `:persistent_session_cookie_opts` - keyword list of cookie options, see
40
40
`Plug.Conn.put_resp_cookie/4` for options. The default options are
41
41
`[max_age: max_age, path: "/"]` where `:max_age` is the value defined in
42
42
`:persistent_session_ttl`.
43
43
44
+ * `:persistent_session_cookie_expiration_timeout` - integer value in
45
+ seconds for how much time should go by before cookie should expire after
46
+ the token is fetched in `authenticate/2`. Defaults to 10.
47
+
44
48
## Custom metadata
45
49
46
50
You can assign a private `:pow_persistent_session_metadata` key in the conn
@@ -71,6 +75,7 @@ defmodule PowPersistentSession.Plug.Cookie do
71
75
alias Pow . { Config , Plug , UUID }
72
76
73
77
@ cookie_key "persistent_session_cookie"
78
+ @ cookie_expiration_timeout 10
74
79
75
80
@ doc """
76
81
Sets a persistent session cookie with an auto generated token.
@@ -136,41 +141,60 @@ defmodule PowPersistentSession.Plug.Cookie do
136
141
@ doc """
137
142
Expires the persistent session cookie.
138
143
139
- If a persistent session cookie exists it'll be expired, and the token in
140
- the persistent session cache will be deleted.
144
+ If a persistent session cookie exists it'll be updated to expire immediately,
145
+ and the token in the persistent session cache will be deleted.
141
146
"""
142
147
@ spec delete ( Conn . t ( ) , Config . t ( ) ) :: Conn . t ( )
143
148
def delete ( conn , config ) do
144
149
cookie_key = cookie_key ( config )
145
150
146
151
case conn . req_cookies [ cookie_key ] do
147
- nil -> conn
148
- key_id -> do_delete ( conn , cookie_key , key_id , config )
152
+ nil ->
153
+ conn
154
+
155
+ key_id ->
156
+ expire_token_in_store ( key_id , config )
157
+ delete_cookie ( conn , cookie_key , config )
149
158
end
150
159
end
151
160
152
- defp do_delete ( conn , cookie_key , key_id , config ) do
161
+ defp expire_token_in_store ( key_id , config ) do
153
162
{ store , store_config } = store ( config )
154
- value = ""
155
- opts = [ max_age: - 1 , path: "/" ]
156
163
157
164
store . delete ( store_config , key_id )
158
- Conn . put_resp_cookie ( conn , cookie_key , value , opts )
165
+ end
166
+
167
+ defp delete_cookie ( conn , cookie_key , config ) do
168
+ opts =
169
+ config
170
+ |> cookie_opts ( )
171
+ |> Keyword . put ( :max_age , - 1 )
172
+
173
+ Conn . put_resp_cookie ( conn , cookie_key , "" , opts )
159
174
end
160
175
161
176
@ doc """
162
177
Authenticates a user with the persistent session cookie.
163
178
164
179
If a persistent session cookie exists, it'll fetch the credentials from the
165
- persistent session cache, and create a new session and persistent session
166
- cookie. The old persistent session cookie and session cache credentials will
167
- be removed.
180
+ persistent session cache.
181
+
182
+ After the value is fetched from the cookie, it'll be updated to expire after
183
+ the value of `:persistent_session_cookie_expiration_timeout` so invalid
184
+ cookies will be deleted eventually. This timeout prevents immediate deletion
185
+ of the cookie so in case of multiple simultaneous requests, the cache has
186
+ time to update the value.
187
+
188
+ If credentials was fetched successfully, the token in the cache is deleted, a
189
+ new session is created, and `create/2` is called to create a new persistent
190
+ session cookie. This will override any expiring cookie.
168
191
169
192
If a `:session_metadata` keyword list is fetched from the persistent session
170
193
metadata, all the values will be merged into the private
171
194
`:pow_session_metadata` key in the conn.
172
195
173
- The cookie expiration will automatically be renewed on every request.
196
+ The expiration date for the cookie will be reset on each request where a user
197
+ is assigned to the conn.
174
198
"""
175
199
@ spec authenticate ( Conn . t ( ) , Config . t ( ) ) :: Conn . t ( )
176
200
def authenticate ( conn , config ) do
@@ -187,23 +211,39 @@ defmodule PowPersistentSession.Plug.Cookie do
187
211
188
212
case conn . req_cookies [ cookie_key ] do
189
213
nil -> conn
190
- key_id -> do_authenticate ( conn , key_id , config )
214
+ key_id -> do_authenticate ( conn , cookie_key , key_id , config )
191
215
end
192
216
end
193
217
defp maybe_authenticate ( conn , _user , _config ) , do: conn
194
218
195
- defp do_authenticate ( conn , key_id , config ) do
219
+ defp do_authenticate ( conn , cookie_key , key_id , config ) do
196
220
{ store , store_config } = store ( config )
197
221
res = store . get ( store_config , key_id )
198
222
plug = Plug . get_plug ( config )
199
- conn = delete ( conn , config )
223
+ conn = expire_cookie ( conn , cookie_key , key_id , config )
200
224
201
225
case res do
202
- :not_found -> conn
203
- res -> fetch_and_auth_user ( conn , res , plug , config )
226
+ :not_found ->
227
+ conn
228
+
229
+ res ->
230
+ expire_token_in_store ( key_id , config )
231
+
232
+ fetch_and_auth_user ( conn , res , plug , config )
204
233
end
205
234
end
206
235
236
+ defp expire_cookie ( conn , cookie_key , key_id , config ) do
237
+ max_age = Config . get ( config , :persistent_session_cookie_expiration_timeout , @ cookie_expiration_timeout )
238
+ opts =
239
+ config
240
+ |> cookie_opts ( )
241
+ |> Keyword . put ( :max_age , max_age )
242
+
243
+ Conn . put_resp_cookie ( conn , cookie_key , key_id , opts )
244
+ end
245
+
246
+
207
247
defp fetch_and_auth_user ( conn , { clauses , metadata } , plug , config ) do
208
248
clauses
209
249
|> filter_invalid! ( )
@@ -276,11 +316,13 @@ defmodule PowPersistentSession.Plug.Cookie do
276
316
end
277
317
278
318
defp maybe_renew ( conn , config ) do
279
- cookie_key = cookie_key ( config )
319
+ cookie_key = cookie_key ( config )
280
320
281
- case conn . resp_cookies [ cookie_key ] do
282
- nil -> renew ( conn , cookie_key , config )
283
- _any -> conn
321
+ with user when not is_nil ( user ) <- Plug . current_user ( conn , config ) ,
322
+ nil <- conn . resp_cookies [ cookie_key ] do
323
+ renew ( conn , cookie_key , config )
324
+ else
325
+ _ -> conn
284
326
end
285
327
end
286
328
0 commit comments