Initial pass of code.
Showing
7 changed files
with
875 additions
and
0 deletions
package-lock.json
0 → 100644
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 | } |
package.json
0 → 100644
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 | } |
src/astro-integration.mjs
0 → 100644
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 | } |
src/fix-racy-store.mjs
0 → 100644
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 |
src/index.mjs
0 → 100644
src/oidc-middleware.mjs
0 → 100644
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 | } |
src/options.mjs
0 → 100644
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 |
-
Please register or sign in to post a comment