d7be2e0c by Adam Heath

Initial pass of code.

1 parent 52c693a0
1 {
2 "name": "astro-oidc-middleware",
3 "lockfileVersion": 3,
4 "requires": true,
5 "packages": {
6 "": {
7 "name": "astro-oidc-middleware",
8 "dependencies": {
9 "cors": "^2.8.5",
10 "express-session": "^1.18.0",
11 "openid-client": "^5.6.5",
12 "passport": "^0.7.0",
13 "read-env": "^2.0.0",
14 "redirecter": "^0.2.3",
15 "session-file-store": "^1.5.0"
16 }
17 },
18 "node_modules/@types/camelcase": {
19 "version": "5.2.0",
20 "resolved": "https://registry.npmjs.org/@types/camelcase/-/camelcase-5.2.0.tgz",
21 "integrity": "sha512-zhHaryYYUUsJ1h6Rq4hisPkljY7c2bkC5PFYQbom5fyKloGJEDK+wdsw2L4hnBwXr4plGjW6D/UVJBbNbOzVpQ==",
22 "deprecated": "This is a stub types definition. camelcase provides its own type definitions, so you do not need this installed.",
23 "dependencies": {
24 "camelcase": "*"
25 }
26 },
27 "node_modules/asn1.js": {
28 "version": "5.4.1",
29 "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz",
30 "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==",
31 "dependencies": {
32 "bn.js": "^4.0.0",
33 "inherits": "^2.0.1",
34 "minimalistic-assert": "^1.0.0",
35 "safer-buffer": "^2.1.0"
36 }
37 },
38 "node_modules/bagpipe": {
39 "version": "0.3.5",
40 "resolved": "https://registry.npmjs.org/bagpipe/-/bagpipe-0.3.5.tgz",
41 "integrity": "sha512-42sAlmPDKes1nLm/aly+0VdaopSU9br+jkRELedhQxI5uXHgtk47I83Mpmf4zoNTRMASdLFtUkimlu/Z9zQ8+g=="
42 },
43 "node_modules/bn.js": {
44 "version": "4.12.0",
45 "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz",
46 "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA=="
47 },
48 "node_modules/camelcase": {
49 "version": "5.3.1",
50 "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
51 "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
52 "engines": {
53 "node": ">=6"
54 }
55 },
56 "node_modules/cookie": {
57 "version": "0.6.0",
58 "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
59 "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
60 "engines": {
61 "node": ">= 0.6"
62 }
63 },
64 "node_modules/cookie-signature": {
65 "version": "1.0.7",
66 "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
67 "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="
68 },
69 "node_modules/cors": {
70 "version": "2.8.5",
71 "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
72 "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
73 "dependencies": {
74 "object-assign": "^4",
75 "vary": "^1"
76 },
77 "engines": {
78 "node": ">= 0.10"
79 }
80 },
81 "node_modules/debug": {
82 "version": "2.6.9",
83 "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
84 "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
85 "dependencies": {
86 "ms": "2.0.0"
87 }
88 },
89 "node_modules/depd": {
90 "version": "2.0.0",
91 "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
92 "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
93 "engines": {
94 "node": ">= 0.8"
95 }
96 },
97 "node_modules/express-session": {
98 "version": "1.18.0",
99 "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.0.tgz",
100 "integrity": "sha512-m93QLWr0ju+rOwApSsyso838LQwgfs44QtOP/WBiwtAgPIo/SAh1a5c6nn2BR6mFNZehTpqKDESzP+fRHVbxwQ==",
101 "dependencies": {
102 "cookie": "0.6.0",
103 "cookie-signature": "1.0.7",
104 "debug": "2.6.9",
105 "depd": "~2.0.0",
106 "on-headers": "~1.0.2",
107 "parseurl": "~1.3.3",
108 "safe-buffer": "5.2.1",
109 "uid-safe": "~2.1.5"
110 },
111 "engines": {
112 "node": ">= 0.8.0"
113 }
114 },
115 "node_modules/fs-extra": {
116 "version": "8.1.0",
117 "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
118 "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
119 "dependencies": {
120 "graceful-fs": "^4.2.0",
121 "jsonfile": "^4.0.0",
122 "universalify": "^0.1.0"
123 },
124 "engines": {
125 "node": ">=6 <7 || >=8"
126 }
127 },
128 "node_modules/graceful-fs": {
129 "version": "4.2.11",
130 "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
131 "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
132 },
133 "node_modules/imurmurhash": {
134 "version": "0.1.4",
135 "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
136 "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
137 "engines": {
138 "node": ">=0.8.19"
139 }
140 },
141 "node_modules/inherits": {
142 "version": "2.0.4",
143 "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
144 "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
145 },
146 "node_modules/is-typedarray": {
147 "version": "1.0.0",
148 "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
149 "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA=="
150 },
151 "node_modules/jose": {
152 "version": "4.15.5",
153 "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.5.tgz",
154 "integrity": "sha512-jc7BFxgKPKi94uOvEmzlSWFFe2+vASyXaKUpdQKatWAESU2MWjDfFf0fdfc83CDKcA5QecabZeNLyfhe3yKNkg==",
155 "funding": {
156 "url": "https://github.com/sponsors/panva"
157 }
158 },
159 "node_modules/jsonfile": {
160 "version": "4.0.0",
161 "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
162 "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
163 "optionalDependencies": {
164 "graceful-fs": "^4.1.6"
165 }
166 },
167 "node_modules/kruptein": {
168 "version": "2.2.3",
169 "resolved": "https://registry.npmjs.org/kruptein/-/kruptein-2.2.3.tgz",
170 "integrity": "sha512-BTwprBPTzkFT9oTugxKd3WnWrX630MqUDsnmBuoa98eQs12oD4n4TeI0GbpdGcYn/73Xueg2rfnw+oK4dovnJg==",
171 "dependencies": {
172 "asn1.js": "^5.4.1"
173 },
174 "engines": {
175 "node": ">6"
176 }
177 },
178 "node_modules/lru-cache": {
179 "version": "6.0.0",
180 "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
181 "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
182 "dependencies": {
183 "yallist": "^4.0.0"
184 },
185 "engines": {
186 "node": ">=10"
187 }
188 },
189 "node_modules/media-types": {
190 "version": "1.0.3",
191 "resolved": "https://registry.npmjs.org/media-types/-/media-types-1.0.3.tgz",
192 "integrity": "sha512-f7UGJB6IeoN/ko8ZeYmNKEuauh6W3cQzm+n4olfhGc0wAPxhSkKcD/RDpG5u+ypkl9trVxBVQnFijyUzYvfJ/Q==",
193 "dependencies": {
194 "negotiator": "~0.2.5"
195 }
196 },
197 "node_modules/minimalistic-assert": {
198 "version": "1.0.1",
199 "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
200 "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
201 },
202 "node_modules/ms": {
203 "version": "2.0.0",
204 "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
205 "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
206 },
207 "node_modules/negotiator": {
208 "version": "0.2.8",
209 "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.2.8.tgz",
210 "integrity": "sha512-2iv1EafEsegrlyCHYPn4bMKM0g5wVTNqkdp8AqOggvSLV5znbQfTASWh4eKBqwEcw1awuY8l3U7wX95JSQWFEg==",
211 "engines": {
212 "node": "*"
213 }
214 },
215 "node_modules/object-assign": {
216 "version": "4.1.1",
217 "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
218 "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
219 "engines": {
220 "node": ">=0.10.0"
221 }
222 },
223 "node_modules/object-hash": {
224 "version": "2.2.0",
225 "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz",
226 "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==",
227 "engines": {
228 "node": ">= 6"
229 }
230 },
231 "node_modules/oidc-token-hash": {
232 "version": "5.0.3",
233 "resolved": "https://registry.npmjs.org/oidc-token-hash/-/oidc-token-hash-5.0.3.tgz",
234 "integrity": "sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==",
235 "engines": {
236 "node": "^10.13.0 || >=12.0.0"
237 }
238 },
239 "node_modules/on-headers": {
240 "version": "1.0.2",
241 "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
242 "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
243 "engines": {
244 "node": ">= 0.8"
245 }
246 },
247 "node_modules/openid-client": {
248 "version": "5.6.5",
249 "resolved": "https://registry.npmjs.org/openid-client/-/openid-client-5.6.5.tgz",
250 "integrity": "sha512-5P4qO9nGJzB5PI0LFlhj4Dzg3m4odt0qsJTfyEtZyOlkgpILwEioOhVVJOrS1iVH494S4Ee5OCjjg6Bf5WOj3w==",
251 "dependencies": {
252 "jose": "^4.15.5",
253 "lru-cache": "^6.0.0",
254 "object-hash": "^2.2.0",
255 "oidc-token-hash": "^5.0.3"
256 },
257 "funding": {
258 "url": "https://github.com/sponsors/panva"
259 }
260 },
261 "node_modules/parseurl": {
262 "version": "1.3.3",
263 "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
264 "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
265 "engines": {
266 "node": ">= 0.8"
267 }
268 },
269 "node_modules/passport": {
270 "version": "0.7.0",
271 "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz",
272 "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==",
273 "dependencies": {
274 "passport-strategy": "1.x.x",
275 "pause": "0.0.1",
276 "utils-merge": "^1.0.1"
277 },
278 "engines": {
279 "node": ">= 0.4.0"
280 },
281 "funding": {
282 "type": "github",
283 "url": "https://github.com/sponsors/jaredhanson"
284 }
285 },
286 "node_modules/passport-strategy": {
287 "version": "1.0.0",
288 "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
289 "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==",
290 "engines": {
291 "node": ">= 0.4.0"
292 }
293 },
294 "node_modules/pause": {
295 "version": "0.0.1",
296 "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
297 "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg=="
298 },
299 "node_modules/random-bytes": {
300 "version": "1.0.0",
301 "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
302 "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
303 "engines": {
304 "node": ">= 0.8"
305 }
306 },
307 "node_modules/read-env": {
308 "version": "2.0.0",
309 "resolved": "https://registry.npmjs.org/read-env/-/read-env-2.0.0.tgz",
310 "integrity": "sha512-gCuo85JH2aWt23HVeFzUUhpLzCRtc9O0jjvpMDG/3vue2aOp7/XllAQjSyL8xGqBifGNYbnWLE0PAqi4AWMBlA==",
311 "dependencies": {
312 "@types/camelcase": "5.2.0",
313 "camelcase": "5.3.1"
314 }
315 },
316 "node_modules/redirecter": {
317 "version": "0.2.3",
318 "resolved": "https://registry.npmjs.org/redirecter/-/redirecter-0.2.3.tgz",
319 "integrity": "sha512-KxZQ+TR3XhbeaMVBcY59TBSDnyNAjAyl7g2a2vwTgjCaq+ZmhmE4GN1w5JI2uMqaIMdPH4kcQIhXW7OcKqIGXQ==",
320 "dependencies": {
321 "media-types": "~1.0.3",
322 "send-data": "~3.1.2"
323 }
324 },
325 "node_modules/retry": {
326 "version": "0.12.0",
327 "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
328 "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
329 "engines": {
330 "node": ">= 4"
331 }
332 },
333 "node_modules/safe-buffer": {
334 "version": "5.2.1",
335 "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
336 "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
337 "funding": [
338 {
339 "type": "github",
340 "url": "https://github.com/sponsors/feross"
341 },
342 {
343 "type": "patreon",
344 "url": "https://www.patreon.com/feross"
345 },
346 {
347 "type": "consulting",
348 "url": "https://feross.org/support"
349 }
350 ]
351 },
352 "node_modules/safer-buffer": {
353 "version": "2.1.2",
354 "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
355 "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
356 },
357 "node_modules/send-data": {
358 "version": "3.1.2",
359 "resolved": "https://registry.npmjs.org/send-data/-/send-data-3.1.2.tgz",
360 "integrity": "sha512-x6nl8B/i2VjihpIgifiXvRaHhHl49ycm78BInqBtCqVh61qPNcWd5zLCDtsHJAFo/gW4nAtljiEIss1fcMw3SQ=="
361 },
362 "node_modules/session-file-store": {
363 "version": "1.5.0",
364 "resolved": "https://registry.npmjs.org/session-file-store/-/session-file-store-1.5.0.tgz",
365 "integrity": "sha512-60IZaJNzyu2tIeHutkYE8RiXVx3KRvacOxfLr2Mj92SIsRIroDsH0IlUUR6fJAjoTW4RQISbaOApa2IZpIwFdQ==",
366 "dependencies": {
367 "bagpipe": "^0.3.5",
368 "fs-extra": "^8.0.1",
369 "kruptein": "^2.0.4",
370 "object-assign": "^4.1.1",
371 "retry": "^0.12.0",
372 "write-file-atomic": "3.0.3"
373 },
374 "engines": {
375 "node": ">= 6"
376 }
377 },
378 "node_modules/signal-exit": {
379 "version": "3.0.7",
380 "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
381 "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
382 },
383 "node_modules/typedarray-to-buffer": {
384 "version": "3.1.5",
385 "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
386 "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
387 "dependencies": {
388 "is-typedarray": "^1.0.0"
389 }
390 },
391 "node_modules/uid-safe": {
392 "version": "2.1.5",
393 "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
394 "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
395 "dependencies": {
396 "random-bytes": "~1.0.0"
397 },
398 "engines": {
399 "node": ">= 0.8"
400 }
401 },
402 "node_modules/universalify": {
403 "version": "0.1.2",
404 "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
405 "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
406 "engines": {
407 "node": ">= 4.0.0"
408 }
409 },
410 "node_modules/utils-merge": {
411 "version": "1.0.1",
412 "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
413 "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
414 "engines": {
415 "node": ">= 0.4.0"
416 }
417 },
418 "node_modules/vary": {
419 "version": "1.1.2",
420 "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
421 "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
422 "engines": {
423 "node": ">= 0.8"
424 }
425 },
426 "node_modules/write-file-atomic": {
427 "version": "3.0.3",
428 "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz",
429 "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==",
430 "dependencies": {
431 "imurmurhash": "^0.1.4",
432 "is-typedarray": "^1.0.0",
433 "signal-exit": "^3.0.2",
434 "typedarray-to-buffer": "^3.1.5"
435 }
436 },
437 "node_modules/yallist": {
438 "version": "4.0.0",
439 "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
440 "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
441 }
442 }
443 }
1 {
2 "name": "astro-oidc-middleware",
3 "type": "module",
4 "exports": {
5 ".": "./src/index.mjs"
6 },
7 "dependencies": {
8 "cors": "^2.8.5",
9 "express-session": "^1.18.0",
10 "openid-client": "^5.6.5",
11 "passport": "^0.7.0",
12 "read-env": "^2.0.0",
13 "redirecter": "^0.2.3",
14 "session-file-store": "^1.5.0"
15 }
16 }
1 import 'dotenv/config'
2 import oidcMiddleware, { fileStoreOptions } from './oidc-middleware.mjs'
3 import { oidcParams } from './options.mjs'
4
5 const clientLocalsSymbol = Symbol.for("astro.locals")
6
7 export default (options) => {
8 return {
9 name: 'session',
10 hooks: {
11 'astro:server:setup': async ({ server }) => {
12 server.middlewares.use(await oidcMiddleware({ oidc: oidcParams() }, fileStoreOptions))
13 server.middlewares.use((req, res, next) => {
14 req[ clientLocalsSymbol ] = res.locals
15 next()
16 })
17 },
18 },
19 }
20 }
1 import { EventEmitter } from 'node:events'
2 import { isEqual } from 'lodash-es'
3
4 const READ = Symbol('READ')
5 const TOUCH = Symbol('TOUCH')
6 const WRITE = Symbol('WRITE')
7
8 export default function (session) {
9 var Store = session.Store
10
11 function FixRacyStore(options = {}) {
12 var self = this
13
14 Store.call(this, options)
15 this.options = options
16 this.readEvents = new EventEmitter()
17 this.writeEvents = new EventEmitter()
18 this.cache = {}
19 this.clearCache = {}
20 }
21
22 FixRacyStore.prototype.__proto__ = Store.prototype
23
24 FixRacyStore.prototype._setCache = function(sessionId, session, newMode, downgrade) {
25 const { [ sessionId ]: { timerId, writes, mode = READ } = {} } = this.cache
26 //console.log(Date.now() + ':setCache', {sessionId, mode, newMode, downgrade})
27 const newEntry = {timerId, writes, session, mode: (mode === READ || downgrade) ? newMode : mode}
28 this.cache[ sessionId ] = newEntry
29 if (mode === newMode && newMode === TOUCH) {
30 return newEntry
31 }
32 clearTimeout(timerId)
33 if (newEntry.mode !== WRITE) {
34 newEntry.timerId = setTimeout(() => {
35 try {
36 const { [ sessionId ]: { mode } } = this.cache
37 if (mode === TOUCH) {
38 //console.log(Date.now() + ':flushing cache', sessionId)
39 this._flush(sessionId)
40 } else {
41 //console.log(Date.now() + ':clearing cache', sessionId)
42 delete this.cache[ sessionId ]
43 }
44 } catch (e) {
45 console.error(e)
46 }
47 }, 1000)
48 }
49 return newEntry
50 }
51
52 FixRacyStore.prototype.get = function(sessionId, callback = (err, session) => {}) {
53 const { [ sessionId ]: { session } = {} } = this.cache
54 if (session) {
55 //console.log('read from memory', {sessionId})
56 callback(null, session)
57 return
58 }
59 this.readEvents.once(sessionId, callback)
60 if (this.readEvents.listenerCount(sessionId) === 1) {
61 //console.log('issuing get', {sessionId})
62 this.options.store.get(sessionId, (err, session) => {
63 //console.log('got session', {sessionId})
64 if (!this.readEvents.listenerCount(sessionId)) {
65 //console.log('race condition fixed!')
66 }
67 this._setCache(sessionId, session, READ)
68 this.readEvents.emit(sessionId, err, session)
69 })
70 } else {
71 //console.log('get already issued', {sessionId})
72 }
73 }
74
75 FixRacyStore.prototype._flush = function (sessionId, ...callbacks) {
76 const { session } = this.cache[ sessionId ]
77 //console.log(Date.now() + ':writing', {sessionId, callbacks: callbacks.length})
78 this.options.store.set(sessionId, session, (err, session) => {
79 callbacks.forEach(callback => callback(err, session))
80 const { [ sessionId ]: cacheEntry } = this.cache
81 const { writes } = cacheEntry
82 cacheEntry.writes = undefined
83 if (writes === undefined || writes.length === 0) {
84 this._setCache(sessionId, session, READ, true)
85 } else {
86 this._flush(sessionId, ...writes)
87 }
88 })
89 }
90
91 FixRacyStore.prototype.set = function (sessionId, session, callback) {
92 if (isEqual(this.cache[ sessionId ], session)) {
93 //console.log('set:isEqual', {sessionId})
94 callback(null, session)
95 return
96 }
97 const cacheEntry = this._setCache(sessionId, session, WRITE)
98 //console.log(Date.now() + ':set', {sessionId, cacheEntry})
99 this.readEvents.emit(sessionId, null, session)
100 if (cacheEntry.writes === undefined) {
101 cacheEntry.writes = []
102 this._flush(sessionId, callback)
103 } else {
104 cacheEntry.writes.push(callback)
105 }
106 }
107
108 FixRacyStore.prototype.touch = function (sessionId, session, callback) {
109 //console.log(Date.now() + ':touch', {sessionId})
110 const cacheEntry = this._setCache(sessionId, session, TOUCH)
111 callback(null, session)
112 return
113 }
114
115 FixRacyStore.prototype.destroy = function (sessionId, callback) {
116 this.readEvents.removeAllListeners(sessionId)
117 this.options.store.destroy(sessionId, callback)
118 }
119
120 FixRacyStore.prototype.clear = function (callback) {
121 this.readEvents.removeAllListeners()
122 this.options.store.clear(callback)
123 }
124
125 FixRacyStore.prototype.length = function (callback) {
126 this.options.store.length(callback)
127 }
128
129 FixRacyStore.prototype.list = function (callback) {
130 this.options.store.list(callback)
131 }
132
133 FixRacyStore.prototype.expired = function (sessionId, callback) {
134 this.options.store.expired(sessionId, callback)
135 }
136
137 return FixRacyStore
138 }
139
1 export { default as FixRacyStore } from './fix-racy-store.mjs'
2 export { default as oidcMiddleware, fileStoreOptions } from './oidc-middleware.mjs'
3 export { readEnvOptions, oidcParams } from './options.mjs'
4 export { default as AstroIntegration } from './astro-integration.mjs'
1 import express from 'express'
2 import ExpressSession from 'express-session'
3 import fileStore from 'session-file-store'
4 import { merge } from 'lodash-es'
5 import path from 'path'
6 import { URL, fileURLToPath, parse, format } from 'url'
7 import { Issuer, Strategy } from 'openid-client'
8 import passport from 'passport'
9 import cors from 'cors'
10 import qs from 'qs'
11 import redirect from 'redirecter'
12
13 import FixRacyStore from './fix-racy-store.mjs'
14
15 const defaultOptions = {
16 auth: {
17 prefix: '/',
18 login: '/login',
19 loginReturnTo: '/',
20 callback: '/login/callback',
21 logout: '/logout',
22 logoutReturnTo: '/',
23 refreshToken: '/login/refresh/token',
24 tokens: '/login/tokens',
25 },
26 session: {
27 secret: 'some-secret',
28 store: async (options) => new ExpressSession.MemoryStore(),
29 },
30 oidc: {
31 enabled: true,
32 },
33 }
34
35 export const fileStoreOptions = {
36 session: {
37 fileStore: {
38 path: path.join(path.dirname(fileURLToPath(import.meta.url)), '../node_modules/.sessions'),
39 },
40 store: async (options) => {
41 const racyStoreOptions = {
42 store: new (fileStore(ExpressSession))(options.session.fileStore),
43 }
44 return new (FixRacyStore(ExpressSession))(racyStoreOptions)
45 },
46 },
47 }
48
49 const getQuery = (req) => {
50 const { originalUrl } = req
51 const url = new URL(originalUrl, 'http://localhost')
52 const result = {}
53 for(const [ key, value ] of url.searchParams) {
54 result[ key ] = value;
55 }
56 return result;
57 }
58
59 const parseRawToken = (rawToken, def) => {
60 return rawToken ? JSON.parse(Buffer.from(rawToken.split('.')[1], 'base64').toString()) : def
61 }
62
63 export default async (...options) => {
64 options = merge({}, defaultOptions, ...options)
65 console.log('options', options.oidc)
66 const app = express.Router()
67
68 const sessionStore = await options.session.store(options)
69
70 app.use(cors({
71 allowedHeaders: ['authorization', 'content-type'],
72 }))
73 app.use(ExpressSession({
74 secret: options.session.secret,
75 resave: false,
76 saveUninitialized: true,
77 store: sessionStore,
78 }))
79
80 const fetchOpenidClient = async () => {
81 const issuer = await Issuer.discover(options.oidc.issuer)
82 return new issuer.Client({
83 client_id: options.oidc.clientId,
84 client_secret: options.oidc.clientSecret,
85 redirect_uris: [ options.oidc.clientUrlBase + (options.auth.prefix === '/' ? '' : options.auth.prefix) + '/login/callback' ],
86 token_endpoint_auth_method: 'client_secret_post',
87 });
88 }
89
90 let oidcClient
91 const getOpenidClient = async () => {
92 if (!oidcClient) {
93 oidcClient = await fetchOpenidClient()
94 setTimeout(() => oidcClient = null, 60000)
95 }
96 return oidcClient
97 }
98
99 const createPassportUser = (tokenSet, userInfo) => ({ claims: tokenSet.claims(), userInfo, tokenSet })
100 const refreshToken = async (req, force = false) => {
101 const { session } = req
102 const { passport: { user: { tokenSet } = {} } = {} } = session
103 try {
104 if (tokenSet) {
105 const now = Date.now() / 1000
106 const expires_at = tokenSet.expires_at
107 const { exp } = parseRawToken(tokenSet.refresh_token)
108 //console.log('checking refresh', {now, expires_at, exp, force})
109 if (force || now + 60 > expires_at) {
110 //console.log('trying for token refresh', {refresh_token: tokenSet.refresh_token})
111 const oidcClient = await getOpenidClient()
112 const newTokenSet = await oidcClient.refresh(tokenSet.refresh_token)
113 const { exp: newExp } = parseRawToken(newTokenSet.refresh_token)
114 //console.log('new refresh_token', {newTokenSet, newExp})
115 if (newTokenSet) {
116 const newUserInfo = await oidcClient.userinfo(newTokenSet.access_token)
117 delete session.passport
118 session.passport = { user: createPassportUser(newTokenSet, newUserInfo) }
119 }
120 }
121 return session.passport
122 }
123 } catch (e) {
124 console.error(e)
125 await new Promise((resolve, reject) => {
126 req.logout(err => {
127 if (err) {
128 reject(err)
129 } else {
130 resolve()
131 }
132 })
133 })
134 }
135 }
136
137 if (options.oidc.enabled) {
138 const auth = express.Router()
139 if (options.auth.prefix !== '/') {
140 app.use(options.auth.prefix, auth)
141 } else {
142 app.use(auth)
143 }
144 auth.use(passport.initialize())
145 auth.use(passport.session())
146
147 passport.use('oidc', new Strategy({ client: await getOpenidClient()}, (tokenSet, userInfo, done) => {
148 done(null, createPassportUser(tokenSet, userInfo))
149 }))
150 passport.serializeUser((user, done) => {
151 //console.log('serializeUser', user)
152 done(null, user)
153 })
154 passport.deserializeUser((user, done) => {
155 //console.log('deserializeUser', user)
156 done(null, user)
157 })
158 auth.get(options.auth.login, (req, res, next) => {
159 const { returnTo = options.auth.loginReturnTo } = getQuery(req)
160 const state = Buffer.from(JSON.stringify({ returnTo })).toString('base64')
161 passport.authenticate('oidc', { state })(req, res, next)
162 })
163 auth.get(options.auth.callback, passport.authenticate('oidc', { failureRedirect: '/' }), (req, res) => {
164 try {
165 const { state } = getQuery(req)
166 const { returnTo } = state ? JSON.parse(Buffer.from(state, 'base64').toString()) : {}
167 if (typeof returnTo === 'string' && returnTo.startsWith('/')) {
168 return redirect(req, res, returnTo)
169 }
170 } catch (e) {
171 console.error(e)
172 }
173 console.log('callback:redirect to /')
174 redirect(req, res, '/')
175 }, (err, req, res, next) => {
176 if (err) {
177 console.error('callback error', err)
178 return redirect(req, res, '/')
179 }
180 next()
181 })
182 auth.get(options.auth.logout, (req, res, next) => {
183 const { session } = req
184 const { passport: { user: { tokenSet } = {} } = {} } = session
185 const { returnTo = options.auth.logoutReturnTo } = getQuery(req)
186 req.logout(async err => {
187 if (!tokenSet) {
188 return redirect(req, res, returnTo)
189 }
190 const oidcClient = await getOpenidClient()
191 const originalUrl = `http${req.socket.encrypted ? 's' : ''}://${req.headers.host}${req.originalUrl}`
192 const returnToURL = new URL(returnTo, originalUrl)
193 const target = oidcClient.endSessionUrl({ id_token_hint: tokenSet.id_token, post_logout_redirect_uri: returnToURL.toString() })
194 return redirect(req, res, target)
195 })
196 })
197 auth.get(options.auth.refreshToken, async (req, res, next) => {
198 await refreshToken(req, true)
199 const { session } = req
200 const { passport = {} } = session
201 const { user: { tokenSet: { access_token, expires_at } = {} } = {} } = passport
202 res.end(JSON.stringify({ access_token, expires_at }))
203 })
204 auth.get(options.auth.tokens, async (req, res, next) => {
205 const passport = await refreshToken(req, true)
206 if (passport) {
207 const { user: { tokenSet } } = passport
208 const { access_token, expires_at, token_type, id_token } = tokenSet
209 res.writeHead(200)
210 res.end(JSON.stringify({ access_token, expires_at, token_type, id_token }))
211 } else {
212 res.writeHead(401)
213 res.end('')
214 }
215 })
216 }
217 app.use(async (req, res, next) => {
218 await refreshToken(req, false)
219 const { session } = req
220 const { passport: { user: { tokenSet } = {} } = {} } = session
221 res.locals = Object.assign(res.locals || {}, {
222 session,
223 async refreshToken() {
224 return refreshToken(req, true)
225 },
226 })
227 await next()
228 })
229 return app
230 }
1 import { default as _ } from 'lodash'
2 import { readEnv } from 'read-env'
3
4 export const readEnvOptions = {
5 source: process.env,
6 separator: '__',
7 format: 'camelcase',
8 sanitize: {
9 object: false,
10 array: false,
11 bool: true,
12 'int': true,
13 'float': true,
14 },
15 }
16
17 export const oidcParams = () => _.pickBy(_.merge({
18 //bearerOnly: true,
19 }, readEnv('OIDC', readEnvOptions)), (value, key) => {
20 return !_.startsWith(key, 'http')
21 })
22
23