Skip to content

Commit d9e0b0f

Browse files
Store sessions in Crux instead of atom (#99)
Store user sessions in Crux rather than in an atom to allow for multiple instances of site to access the same sessions and to persist sessions between restarts. These changes also update the openid connect tests to assert that the expected sessions are created in the database. The expiry time of sessions created by calling the token endpoint has been extended from 1 hour to 1 week. Sessions created by logging in by basic auth have been reduced to 1 week from 1 month to be consistent.
1 parent 8a1a77b commit d9e0b0f

File tree

7 files changed

+143
-116
lines changed

7 files changed

+143
-116
lines changed

src/juxt/pass/alpha/authentication.clj

+46-36
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
(ns juxt.pass.alpha.authentication
44
(:require
5-
[jsonista.core :as json]
65
[clojure.tools.logging :as log]
7-
[crypto.password.bcrypt :as password]
6+
[clojure.walk :as walk]
87
[crux.api :as x]
8+
[crypto.password.bcrypt :as password]
9+
[jsonista.core :as json]
910
[juxt.reap.alpha.decoders :as reap]
1011
[juxt.reap.alpha.rfc7235 :as rfc7235]
11-
[ring.util.codec :refer [form-decode url-decode]]
12-
[ring.middleware.cookies :refer [cookies-request cookies-response]]))
12+
[ring.middleware.cookies :refer [cookies-request cookies-response]]
13+
[ring.util.codec :refer [form-decode]])
14+
(:import
15+
(java.time Instant)))
1316

1417
(alias 'http (create-ns 'juxt.http.alpha))
1518
(alias 'pass (create-ns 'juxt.pass.alpha))
@@ -23,32 +26,40 @@
2326
(.nextBytes SECURE-RANDOM bytes)
2427
(.encodeToString BASE64-ENCODER bytes)))
2528

26-
(defonce sessions-by-access-token (atom {}))
27-
28-
(defn put-session! [k session ^java.time.Instant expiry-instant]
29-
(swap! sessions-by-access-token
30-
assoc k (assoc session
31-
::expiry-instant expiry-instant)))
32-
33-
(defn remove-session! [k]
34-
(swap! sessions-by-access-token
35-
dissoc k))
36-
37-
(defn expire-sessions! [date-now]
38-
(swap! sessions-by-access-token
39-
(fn [sessions]
40-
(into {} (remove #(.isAfter (.toInstant date-now)
41-
(-> % second (get ::expiry-instant)))
42-
sessions)))))
43-
44-
(defn lookup-session [k date-now]
45-
(expire-sessions! date-now)
46-
(get @sessions-by-access-token k))
47-
48-
;;(identity @sessions-by-access-token)
29+
(defn put-session! [{::site/keys [crux-node base-uri start-date]} k session]
30+
(let [session (walk/keywordize-keys session)]
31+
(->> [[:crux.tx/put
32+
(merge
33+
(select-keys session [::pass/user ::pass/state ::pass/nonce ::pass/return-to])
34+
{:crux.db/id (str base-uri "/site-session/" k)
35+
:juxt.site.alpha/type "SiteSession"
36+
::expiry-instant
37+
(-> (if start-date (.toInstant start-date) (Instant/now))
38+
(.plusSeconds (or (:expires_in session) 3600)))})]]
39+
(x/submit-tx crux-node)
40+
(x/await-tx crux-node))))
41+
42+
(defn remove-session! [{::site/keys [crux-node base-uri]} k]
43+
(->> [[:crux.tx/evict (str base-uri "/site-session/" k)]]
44+
(x/submit-tx crux-node)
45+
(x/await-tx crux-node)))
46+
47+
(defn expire-sessions! [{::site/keys [crux-node db start-date]}]
48+
(->> (x/q db '{:find [ss expiry-instant]
49+
:where [[ss :juxt.site.alpha/type "SiteSession"]
50+
[ss ::expiry-instant expiry-instant]]})
51+
(filter (fn [[_ expiry-instant]]
52+
(.isAfter (.toInstant start-date) expiry-instant)))
53+
(mapv (fn [[ss _]] [:crux.tx/evict ss]))
54+
(x/submit-tx crux-node)
55+
(x/await-tx crux-node)))
56+
57+
(defn lookup-session [{::site/keys [db base-uri] :as req} k]
58+
(expire-sessions! req)
59+
(x/entity db (str base-uri "/site-session/" k)))
4960

5061
(defn token-response
51-
[{::site/keys [received-representation resource start-date]
62+
[{::site/keys [received-representation resource]
5263
::pass/keys [subject] :as req}]
5364

5465
;; Check grant_type of posted-representation
@@ -84,9 +95,9 @@
8495
"user" (::pass/user subject)}
8596

8697
_ (put-session!
98+
req
8799
access-token
88-
(merge session subject)
89-
(.plusSeconds (.toInstant start-date) expires-in))
100+
(merge session subject))
90101

91102
body (.getBytes
92103
(str
@@ -145,10 +156,10 @@
145156
"token_type" "login"
146157
"expires_in" expires-in}]
147158
(put-session!
159+
req
148160
access-token
149161
(merge session {::pass/user user
150-
::pass/username username})
151-
(.plusSeconds (.toInstant start-date) expires-in))
162+
::pass/username username}))
152163
(-> req
153164
(assoc :ring.response/status 302
154165
:ring.response/body
@@ -236,13 +247,12 @@
236247
(some-> req
237248
((fn [req] (assoc req :headers (get req :ring.request/headers))))
238249
cookies-request
239-
:cookies (get "site_session") :value json/read-value)
240-
now (::site/start-date req)]
250+
:cookies (get "site_session") :value json/read-value)]
241251

242252
(or
243253
;; Cookie
244254
(when access-token
245-
(when-let [session (lookup-session access-token now)]
255+
(when-let [session (lookup-session req access-token)]
246256
(->
247257
(select-keys session [::pass/user ::pass/username])
248258
(assoc ::pass/auth-scheme "Session"))))
@@ -276,7 +286,7 @@
276286
(log/error e)))
277287

278288
"bearer"
279-
(when-let [session (lookup-session token68 now)]
289+
(when-let [session (lookup-session req token68)]
280290
(->
281291
(select-keys session [::pass/user ::pass/username])
282292
(assoc ::pass/auth-scheme "Bearer")))

src/juxt/pass/alpha/openid_connect.clj

+5-4
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,13 @@
6464

6565
;; Create a pre-auth session
6666
session-token-id! (make-nonce 16)
67-
expires-in (get resource ::pass/expires-in (* 24 3600))
6867
_ (put-session!
68+
req
6969
session-token-id!
70-
(cond-> {::pass/state state ::pass/nonce nonce}
71-
return-to (assoc ::pass/return-to return-to))
72-
(.plusSeconds (.toInstant start-date) expires-in))
70+
(cond-> {::pass/state state
71+
::pass/nonce nonce
72+
:expires_in (* 24 3600)}
73+
return-to (assoc ::pass/return-to return-to)))
7374

7475
query-string
7576
(codec/form-encode

src/juxt/pass/alpha/session.clj

+15-14
Original file line numberDiff line numberDiff line change
@@ -36,35 +36,36 @@
3636

3737
(defn escalate-session
3838
"If provided session-token-id! matches, create a new session with the matched identity"
39-
[{::site/keys [db crux-node resource start-date]
40-
::pass/keys [session-token-id!] :as req} matched-identity]
41-
(let [session (authz/lookup-session session-token-id! start-date)
39+
[{::pass/keys [session-token-id!] :as req} matched-identity]
40+
(let [session (authz/lookup-session req session-token-id!)
4241
_ (assert session)
43-
new-session-token-id! (authz/access-token)
44-
expires-in (get resource ::pass/expires-in (* 24 3600))]
45-
(authz/remove-session! session-token-id!)
42+
new-session-token-id! (authz/access-token)]
43+
(authz/remove-session! req session-token-id!)
4644
(authz/put-session!
45+
req
4746
new-session-token-id!
48-
{::pass/user matched-identity}
49-
(.plusSeconds (.toInstant start-date) expires-in))
47+
{::pass/user matched-identity
48+
"access_token" new-session-token-id!
49+
"expires_in" (* 24 3600)
50+
"user" matched-identity})
5051

5152
(-> req
5253
(set-cookie new-session-token-id!)
5354
(update-in [:ring.response/headers "location"]
54-
(fn [location]
55-
(-> (new URIBuilder location)
56-
(.addParameter "code" new-session-token-id!)
57-
(.toString)))))))
55+
(fn [location]
56+
(-> (new URIBuilder location)
57+
(.addParameter "code" new-session-token-id!)
58+
(.toString)))))))
5859

5960
(defn wrap-associate-session [h]
60-
(fn [{::site/keys [db start-date] :as req}]
61+
(fn [req]
6162
(let [session-token-id!
6263
(-> (assoc req :headers (get req :ring.request/headers))
6364
cookies-request
6465
:cookies (get "id") :value)
6566

6667
session (when session-token-id!
67-
(authz/lookup-session session-token-id! start-date))
68+
(authz/lookup-session req session-token-id!))
6869

6970
subject (some->
7071
(select-keys session [::pass/user ::pass/username])

src/juxt/site/alpha/init.clj

+2-2
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@
155155
{"http://localhost:8000"
156156
{::site/access-control-allow-methods #{:post}
157157
::site/access-control-allow-headers #{"authorization" "content-type"}}}
158-
::pass/expires-in (* 60 60 1)}
158+
::pass/expires-in (* 3600 24 7)}
159159

160160
{:crux.db/id (str base-uri "/_site/rules/anyone-can-ask-for-a-token")
161161
::site/type "Rule"
@@ -194,7 +194,7 @@
194194
::http/methods #{:post}
195195
::http/acceptable "application/x-www-form-urlencoded"
196196
::site/purpose ::site/login
197-
::pass/expires-in (* 3600 24 30)}
197+
::pass/expires-in (* 3600 24 7)}
198198

199199
{:crux.db/id (str base-uri "/_site/rules/anyone-can-post-login-credentials")
200200
::site/type "Rule"

src/juxt/site/alpha/repl.clj

-7
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,6 @@
168168
(println "Evicting" (count batch) "records")
169169
(println (apply evict! batch))))))
170170

171-
(defn sessions []
172-
(authn/expire-sessions! (java.util.Date.))
173-
(deref authn/sessions-by-access-token))
174-
175-
(defn clear-sessions []
176-
(reset! authn/sessions-by-access-token {}))
177-
178171
(defn superusers
179172
([] (superusers (config)))
180173
([{::site/keys [base-uri]}]

0 commit comments

Comments
 (0)