@oslokommune/auth-bff 1.6.1 → 2.0.0-beta2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +240 -0
  2. package/dist/package.json +58 -0
  3. package/dist/src/OpenIdConfigManager.d.ts +10 -0
  4. package/dist/src/OpenIdConfigManager.d.ts.map +1 -0
  5. package/dist/src/OpenIdConfigManager.js +77 -0
  6. package/dist/src/config.d.ts +128 -0
  7. package/dist/src/config.d.ts.map +1 -0
  8. package/dist/src/config.js +55 -0
  9. package/dist/src/middleware/OidcMiddleware.d.ts +20 -0
  10. package/dist/src/middleware/OidcMiddleware.d.ts.map +1 -0
  11. package/dist/src/middleware/OidcMiddleware.js +232 -0
  12. package/dist/src/middleware/oidc-routes.d.mts.map +1 -0
  13. package/dist/src/middleware/oidc-routes.d.ts +3 -0
  14. package/dist/src/middleware/oidc-routes.d.ts.map +1 -0
  15. package/dist/src/middleware/oidc-routes.js +10 -0
  16. package/dist/src/middleware/proxy-routes.d.mts.map +1 -0
  17. package/dist/src/middleware/proxy-routes.d.ts +4 -0
  18. package/dist/src/middleware/proxy-routes.d.ts.map +1 -0
  19. package/dist/src/middleware/proxy-routes.js +28 -0
  20. package/dist/{middleware → src/middleware}/proxy-routes.mjs +5 -5
  21. package/dist/src/middleware/security-headers.d.mts.map +1 -0
  22. package/dist/src/middleware/security-headers.d.ts +4 -0
  23. package/dist/src/middleware/security-headers.d.ts.map +1 -0
  24. package/dist/src/middleware/security-headers.js +31 -0
  25. package/dist/{middleware → src/middleware}/security-headers.mjs +2 -2
  26. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.mts +3 -0
  27. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.mts.map +1 -0
  28. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.ts +3 -0
  29. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.ts.map +1 -0
  30. package/dist/src/middleware/sessions/dynamoDbSessionStore.js +41 -0
  31. package/dist/{middleware → src/middleware}/sessions/dynamoDbSessionStore.mjs +12 -17
  32. package/dist/src/middleware/sessions/memorySessionStore.d.mts +3 -0
  33. package/dist/src/middleware/sessions/memorySessionStore.d.mts.map +1 -0
  34. package/dist/src/middleware/sessions/memorySessionStore.d.ts +3 -0
  35. package/dist/src/middleware/sessions/memorySessionStore.d.ts.map +1 -0
  36. package/dist/src/middleware/sessions/memorySessionStore.js +11 -0
  37. package/dist/src/middleware/sessions/sessions.d.mts +2 -0
  38. package/dist/src/middleware/sessions/sessions.d.mts.map +1 -0
  39. package/dist/src/middleware/sessions/sessions.d.ts +3 -0
  40. package/dist/src/middleware/sessions/sessions.d.ts.map +1 -0
  41. package/dist/src/middleware/sessions/sessions.js +39 -0
  42. package/dist/{middleware → src/middleware}/sessions/sessions.mjs +3 -4
  43. package/dist/src/middleware/static-routes.d.mts.map +1 -0
  44. package/dist/src/middleware/static-routes.d.ts +3 -0
  45. package/dist/src/middleware/static-routes.d.ts.map +1 -0
  46. package/dist/src/middleware/static-routes.js +19 -0
  47. package/dist/src/react/AuthContext.d.ts.map +1 -0
  48. package/dist/src/react/AuthContextProvider.d.ts.map +1 -0
  49. package/dist/{react → src/react}/AuthContextProvider.jsx +15 -27
  50. package/dist/src/react/UseAuthContext.d.ts +2 -0
  51. package/dist/src/react/UseAuthContext.d.ts.map +1 -0
  52. package/dist/{react → src/react}/UseAuthContext.jsx +2 -2
  53. package/dist/src/react/global-user.d.ts.map +1 -0
  54. package/dist/src/react/index.d.ts +5 -0
  55. package/dist/src/react/index.d.ts.map +1 -0
  56. package/dist/src/react/index.js +4 -0
  57. package/dist/src/react/poller.d.ts.map +1 -0
  58. package/dist/src/react/poller.js +28 -0
  59. package/dist/{server.d.mts.map → src/server.d.mts.map} +1 -1
  60. package/dist/src/server.d.ts +3 -0
  61. package/dist/src/server.d.ts.map +1 -0
  62. package/dist/src/server.js +43 -0
  63. package/dist/{server.mjs → src/server.mjs} +2 -2
  64. package/dist/src/utils.d.ts +2 -0
  65. package/dist/src/utils.d.ts.map +1 -0
  66. package/dist/src/utils.js +3 -0
  67. package/dist/src/vite-plugin.d.mts.map +1 -0
  68. package/dist/src/vite-plugin.d.ts +10 -0
  69. package/dist/src/vite-plugin.d.ts.map +1 -0
  70. package/dist/src/vite-plugin.js +26 -0
  71. package/dist/src/vite-plugin.mjs +35 -0
  72. package/package.json +24 -16
  73. package/dist/client.d.mts +0 -7
  74. package/dist/client.d.mts.map +0 -1
  75. package/dist/client.mjs +0 -92
  76. package/dist/config.d.mts +0 -4
  77. package/dist/config.d.mts.map +0 -1
  78. package/dist/config.mjs +0 -69
  79. package/dist/middleware/oidc-routes.d.mts.map +0 -1
  80. package/dist/middleware/oidc.d.mts +0 -17
  81. package/dist/middleware/oidc.d.mts.map +0 -1
  82. package/dist/middleware/oidc.mjs +0 -220
  83. package/dist/middleware/proxy-routes.d.mts.map +0 -1
  84. package/dist/middleware/security-headers.d.mts.map +0 -1
  85. package/dist/middleware/sessions/dynamoDbSessionStore.d.mts +0 -2
  86. package/dist/middleware/sessions/dynamoDbSessionStore.d.mts.map +0 -1
  87. package/dist/middleware/sessions/memorySessionStore.d.mts +0 -2
  88. package/dist/middleware/sessions/memorySessionStore.d.mts.map +0 -1
  89. package/dist/middleware/sessions/sessions.d.mts +0 -2
  90. package/dist/middleware/sessions/sessions.d.mts.map +0 -1
  91. package/dist/middleware/static-routes.d.mts.map +0 -1
  92. package/dist/react/AuthContext.d.ts.map +0 -1
  93. package/dist/react/AuthContextProvider.d.ts.map +0 -1
  94. package/dist/react/UseAuthContext.d.ts +0 -2
  95. package/dist/react/UseAuthContext.d.ts.map +0 -1
  96. package/dist/react/global-user.d.ts.map +0 -1
  97. package/dist/react/index.d.ts +0 -5
  98. package/dist/react/index.d.ts.map +0 -1
  99. package/dist/react/index.js +0 -4
  100. package/dist/react/poller.d.ts.map +0 -1
  101. package/dist/react/poller.js +0 -39
  102. package/dist/utils.d.ts +0 -2
  103. package/dist/utils.d.ts.map +0 -1
  104. package/dist/utils.js +0 -3
  105. package/dist/vite-plugin.d.mts.map +0 -1
  106. package/dist/vite-plugin.mjs +0 -44
  107. /package/dist/{middleware → src/middleware}/oidc-routes.d.mts +0 -0
  108. /package/dist/{middleware → src/middleware}/oidc-routes.mjs +0 -0
  109. /package/dist/{middleware → src/middleware}/proxy-routes.d.mts +0 -0
  110. /package/dist/{middleware → src/middleware}/security-headers.d.mts +0 -0
  111. /package/dist/{middleware → src/middleware}/sessions/memorySessionStore.mjs +0 -0
  112. /package/dist/{middleware → src/middleware}/static-routes.d.mts +0 -0
  113. /package/dist/{middleware → src/middleware}/static-routes.mjs +0 -0
  114. /package/dist/{react → src/react}/AuthContext.d.ts +0 -0
  115. /package/dist/{react → src/react}/AuthContext.jsx +0 -0
  116. /package/dist/{react → src/react}/AuthContextProvider.d.ts +0 -0
  117. /package/dist/{react → src/react}/global-user.d.ts +0 -0
  118. /package/dist/{react → src/react}/global-user.js +0 -0
  119. /package/dist/{react → src/react}/poller.d.ts +0 -0
  120. /package/dist/{server.d.mts → src/server.d.mts} +0 -0
  121. /package/dist/{vite-plugin.d.mts → src/vite-plugin.d.mts} +0 -0
@@ -0,0 +1,232 @@
1
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
2
+ if (kind === "m") throw new TypeError("Private method is not writable");
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
5
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
6
+ };
7
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
8
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
9
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
+ };
12
+ var _OidcMiddleware_instances, _OidcMiddleware_configManager, _OidcMiddleware_bffConfig, _OidcMiddleware_refreshPromises, _OidcMiddleware_openIdConfig_get, _OidcMiddleware_refreshTokens, _OidcMiddleware_getFreshTokens, _OidcMiddleware_getUserClaims, _OidcMiddleware_getAccessTokenExpiryTime;
13
+ import * as openIdClient from "openid-client";
14
+ import { OpenIdConfigManager } from "../OpenIdConfigManager.js";
15
+ import { redact } from "../utils.js";
16
+ export class OidcMiddleware {
17
+ /**
18
+ * @private
19
+ * @param config
20
+ * @param configManager
21
+ */
22
+ constructor(config, configManager) {
23
+ _OidcMiddleware_instances.add(this);
24
+ _OidcMiddleware_configManager.set(this, void 0);
25
+ _OidcMiddleware_bffConfig.set(this, void 0);
26
+ _OidcMiddleware_refreshPromises.set(this, {}
27
+ /**
28
+ * @private
29
+ * @param config
30
+ * @param configManager
31
+ */
32
+ );
33
+ __classPrivateFieldSet(this, _OidcMiddleware_configManager, configManager, "f");
34
+ __classPrivateFieldSet(this, _OidcMiddleware_bffConfig, config, "f");
35
+ }
36
+ static async create(config) {
37
+ const configManager = new OpenIdConfigManager(config);
38
+ await configManager.init();
39
+ return new OidcMiddleware(config, configManager);
40
+ }
41
+ get ensureFreshToken() {
42
+ return (req, res, next) => {
43
+ __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getFreshTokens).call(this, req).then(tokenResponse => {
44
+ if (tokenResponse) {
45
+ req.tokenResponse = tokenResponse;
46
+ next();
47
+ }
48
+ else {
49
+ console.warn(`401: No valid tokens in session sid=${redact(req.session.id)}`);
50
+ res.sendStatus(401);
51
+ }
52
+ }).catch(next);
53
+ };
54
+ }
55
+ get login() {
56
+ return async (req, res, next) => {
57
+ try {
58
+ const codeVerifier = openIdClient.randomPKCECodeVerifier();
59
+ const codeChallenge = await openIdClient.calculatePKCECodeChallenge(codeVerifier);
60
+ const stateKey = openIdClient.randomState();
61
+ const redirectUrl = req.query.redirectUrl; //TODO: håndtering av andre typer her?
62
+ const params = new URLSearchParams();
63
+ params.append('scope', "openid profile");
64
+ params.append('code_challenge', codeChallenge);
65
+ params.append('code_challenge_method', 'S256');
66
+ params.append('redirect_uri', __classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").redirectUri);
67
+ params.append('state', stateKey);
68
+ __classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").resources?.forEach(resource => {
69
+ params.append('resource', resource);
70
+ });
71
+ const authorizationUrl = openIdClient.buildAuthorizationUrl(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), params);
72
+ req.session.codeVerifier = codeVerifier;
73
+ req.session.stateKey = stateKey;
74
+ req.session.stateValue = { redirectUrl };
75
+ req.session.save(() => {
76
+ res.redirect(authorizationUrl.toString());
77
+ });
78
+ }
79
+ catch (e) {
80
+ console.error(e);
81
+ next(e);
82
+ }
83
+ };
84
+ }
85
+ get callback() {
86
+ return async (req, res, next) => {
87
+ try {
88
+ const { codeVerifier, stateKey, stateValue } = req.session;
89
+ const url = new URL(`${req.protocol}://${req.headers.host}${req.originalUrl}`);
90
+ const tokenResponse = await openIdClient.authorizationCodeGrant(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), url, {
91
+ expectedState: stateKey,
92
+ pkceCodeVerifier: codeVerifier
93
+ });
94
+ req.session.tokenResponse = tokenResponse;
95
+ req.session["idp-sid"] = tokenResponse.claims().sid;
96
+ req.session.userClaims = __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getUserClaims).call(this, tokenResponse);
97
+ req.session.accessTokenExpiresAt = __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getAccessTokenExpiryTime).call(this, tokenResponse);
98
+ delete req.session.codeVerifier;
99
+ delete req.session.stateKey;
100
+ delete req.session.stateValue;
101
+ req.session.save(() => {
102
+ let redirectUrl = stateValue.redirectUrl;
103
+ //only allow relative redirecturls:
104
+ const absoluteUrlRegex = /^(?:[a-z+]+:)?\/\//;
105
+ if (!redirectUrl || absoluteUrlRegex.test(redirectUrl)) {
106
+ redirectUrl = __classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").basePath || "/";
107
+ }
108
+ res.redirect(redirectUrl);
109
+ });
110
+ }
111
+ catch (e) {
112
+ console.error(e);
113
+ req.session.destroy(() => {
114
+ next(e);
115
+ });
116
+ }
117
+ };
118
+ }
119
+ get user() {
120
+ return async (req, res, next) => {
121
+ try {
122
+ const tokenResponse = await __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getFreshTokens).call(this, req);
123
+ if (!tokenResponse) {
124
+ console.log('/user 401: No tokenset');
125
+ return res.sendStatus(401);
126
+ }
127
+ return res.send(req.session.userClaims);
128
+ }
129
+ catch (e) {
130
+ console.error(`Error in /user sid=${redact(req.session?.id)}`, e);
131
+ next(e);
132
+ }
133
+ };
134
+ }
135
+ get logout() {
136
+ return (req, res) => {
137
+ const tokenResponse = req.session.tokenResponse;
138
+ req.session.destroy(() => {
139
+ const endSessionUrl = openIdClient.buildEndSessionUrl(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), {
140
+ id_token_hint: tokenResponse?.id_token,
141
+ });
142
+ res.redirect(endSessionUrl.toString());
143
+ });
144
+ };
145
+ }
146
+ get frontChannelLogout() {
147
+ return async (req, res) => {
148
+ const { iss, sid } = req.query;
149
+ console.log(`Front channel logout: params iss=${iss}, sid=${redact(sid)}`);
150
+ if (sid) {
151
+ try {
152
+ await req.destroySessionByIdpSid?.(sid);
153
+ }
154
+ catch (e) {
155
+ console.error("Failed to destroy session", e);
156
+ }
157
+ }
158
+ res.sendStatus(200);
159
+ };
160
+ }
161
+ }
162
+ _OidcMiddleware_configManager = new WeakMap(), _OidcMiddleware_bffConfig = new WeakMap(), _OidcMiddleware_refreshPromises = new WeakMap(), _OidcMiddleware_instances = new WeakSet(), _OidcMiddleware_openIdConfig_get = function _OidcMiddleware_openIdConfig_get() {
163
+ return __classPrivateFieldGet(this, _OidcMiddleware_configManager, "f").openIdConfig;
164
+ }, _OidcMiddleware_refreshTokens = async function _OidcMiddleware_refreshTokens(req, tokenResponse) {
165
+ var _a;
166
+ const sessionId = req.session.id;
167
+ const refreshToken = tokenResponse.refresh_token;
168
+ const doRefresh = async () => {
169
+ console.log(`Token refresh starting. sid=${redact(sessionId)}`);
170
+ try {
171
+ const tokenResponse = await openIdClient.refreshTokenGrant(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), refreshToken);
172
+ console.log(`Token refresh OK. sid=${redact(sessionId)}`);
173
+ return tokenResponse;
174
+ }
175
+ catch (err) {
176
+ console.log(`Token refresh failed. sid=${redact(sessionId)}`, err);
177
+ return null;
178
+ }
179
+ };
180
+ const refreshPromise = (_a = __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f"))[refreshToken] ?? (_a[refreshToken] = doRefresh().finally(() => {
181
+ console.log(`Token refresh finished. Cleaning up. sid=${redact(sessionId)}`);
182
+ setTimeout(() => {
183
+ delete __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f")[refreshToken];
184
+ }, 10000);
185
+ }));
186
+ const refreshedTokenResponse = await refreshPromise;
187
+ if (refreshedTokenResponse) {
188
+ Object.assign(req.session.tokenResponse, refreshedTokenResponse);
189
+ req.session.accessTokenExpiresAt = __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getAccessTokenExpiryTime).call(this, refreshedTokenResponse);
190
+ }
191
+ else {
192
+ req.session.tokenResponse = null;
193
+ }
194
+ return req.session.tokenResponse;
195
+ }, _OidcMiddleware_getFreshTokens = async function _OidcMiddleware_getFreshTokens(req) {
196
+ const tokenResponse = req.session.tokenResponse;
197
+ if (!tokenResponse) {
198
+ console.log(`No tokenResponse found in session sid=${redact(req.session.id)}`);
199
+ return;
200
+ }
201
+ const now = new Date().getTime();
202
+ const expiresAt = req.session.accessTokenExpiresAt || 0;
203
+ /*if(!expiresAt) {
204
+ //For at ting ikke skal eksplodere hvis man får inn en gammel session.
205
+ //TODO: denne kan fjernes når den har kjørt i prod i et døgn+
206
+ console.error('accessTokenExpiresAt was not set')
207
+ return
208
+ }*/
209
+ const expiresInSeconds = (expiresAt - now) / 1000;
210
+ if (expiresInSeconds < 5) {
211
+ console.log(`Access token expired. sid=${redact(req.session.id)}, expiresInSeconds=${expiresInSeconds}`);
212
+ return await __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_refreshTokens).call(this, req, tokenResponse);
213
+ }
214
+ else {
215
+ return tokenResponse;
216
+ }
217
+ }, _OidcMiddleware_getUserClaims = function _OidcMiddleware_getUserClaims(tokenResponse) {
218
+ let claims = tokenResponse.claims();
219
+ if (__classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").userClaims) {
220
+ claims = __classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").userClaims.reduce((acc, claim) => {
221
+ acc[claim] = claims[claim];
222
+ return acc;
223
+ }, {});
224
+ }
225
+ return claims;
226
+ }, _OidcMiddleware_getAccessTokenExpiryTime = function _OidcMiddleware_getAccessTokenExpiryTime(tokenResponse) {
227
+ if (tokenResponse.expires_in !== undefined) {
228
+ const now = new Date();
229
+ now.setSeconds(now.getSeconds() + tokenResponse.expires_in);
230
+ return now.getTime();
231
+ }
232
+ };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-routes.d.mts","sourceRoot":"","sources":["../../../src/middleware/oidc-routes.mjs"],"names":[],"mappings":"AAEA,qDAUC"}
@@ -0,0 +1,3 @@
1
+ import { OidcMiddleware } from "./OidcMiddleware.js";
2
+ export declare function oidcRoutes(oidcMiddleware: OidcMiddleware): import("express-serve-static-core").Router;
3
+ //# sourceMappingURL=oidc-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oidc-routes.d.ts","sourceRoot":"","sources":["../../../src/middleware/oidc-routes.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD,wBAAgB,UAAU,CAAC,cAAc,EAAE,cAAc,8CAUxD"}
@@ -0,0 +1,10 @@
1
+ import express from "express";
2
+ export function oidcRoutes(oidcMiddleware) {
3
+ const router = express.Router();
4
+ router.get('/auth/login', oidcMiddleware.login);
5
+ router.get('/auth/callback', oidcMiddleware.callback);
6
+ router.get('/auth/logout', oidcMiddleware.logout);
7
+ router.get('/auth/user', oidcMiddleware.user);
8
+ router.get('/auth/front-channel-logout', oidcMiddleware.frontChannelLogout);
9
+ return router;
10
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-routes.d.mts","sourceRoot":"","sources":["../../../src/middleware/proxy-routes.mjs"],"names":[],"mappings":"AAGA,mEA4BC"}
@@ -0,0 +1,4 @@
1
+ import { BffConfig } from "../config.js";
2
+ import { OidcMiddleware } from "./OidcMiddleware.js";
3
+ export declare function proxyRoutes(config: BffConfig, oidcMiddleware: OidcMiddleware): import("express-serve-static-core").Router;
4
+ //# sourceMappingURL=proxy-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proxy-routes.d.ts","sourceRoot":"","sources":["../../../src/middleware/proxy-routes.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,EAAC,cAAc,EAAC,MAAM,qBAAqB,CAAC;AAEnD,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,8CA6B5E"}
@@ -0,0 +1,28 @@
1
+ import express from "express";
2
+ import { createProxyMiddleware } from "http-proxy-middleware";
3
+ export function proxyRoutes(config, oidcMiddleware) {
4
+ const router = express.Router();
5
+ for (const [path, target] of Object.entries(config.proxyTargets)) {
6
+ console.log(`Setting up auth proxy: ${path} -> ${target}`);
7
+ router.use(path, oidcMiddleware.ensureFreshToken, createProxyMiddleware({
8
+ target: target,
9
+ changeOrigin: true,
10
+ on: {
11
+ proxyReq: (proxyReq, req) => {
12
+ const accessToken = req.tokenResponse?.access_token;
13
+ if (!accessToken) {
14
+ console.error("proxy: missing token");
15
+ return;
16
+ }
17
+ proxyReq.setHeader("Authorization", `Bearer ${accessToken}`);
18
+ proxyReq.removeHeader("Cookie");
19
+ },
20
+ proxyRes: (proxyRes, req) => {
21
+ // @ts-ignore //TODO: proxyRes har en mystisk type som mangler req, men den er der
22
+ console.log(`Proxied: ${req.method} ${req.originalUrl} -> ${proxyRes.req.protocol}//${proxyRes.req.host}${proxyRes.req.path}, status=${proxyRes.statusCode}`);
23
+ }
24
+ }
25
+ }));
26
+ }
27
+ return router;
28
+ }
@@ -9,16 +9,16 @@ export function proxyRoutes(config, oidcMiddleware) {
9
9
  changeOrigin: true,
10
10
  on: {
11
11
  proxyReq: (proxyReq, req, res) => {
12
- const tokenSet = req.tokenSet;
13
- if (!tokenSet) {
14
- console.error("proxy: missing tokenSet");
12
+ const accessToken = req.tokenResponse?.access_token;
13
+ if (!accessToken) {
14
+ console.error("proxy: missing token");
15
15
  return;
16
16
  }
17
- proxyReq.setHeader("Authorization", `Bearer ${tokenSet.access_token}`);
17
+ proxyReq.setHeader("Authorization", `Bearer ${accessToken}`);
18
18
  proxyReq.removeHeader("Cookie");
19
19
  },
20
20
  proxyRes: (proxyRes, req, res) => {
21
- console.log(`Proxied ${req.originalUrl}: ${proxyRes.statusCode}`);
21
+ console.log(`Proxied: ${req.method} ${req.originalUrl} -> ${proxyRes.req.protocol}//${proxyRes.req.host}${proxyRes.req.path}, status=${proxyRes.statusCode}`);
22
22
  }
23
23
  }
24
24
  }));
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.d.mts","sourceRoot":"","sources":["../../../src/middleware/security-headers.mjs"],"names":[],"mappings":"AAGA,0FA+BC"}
@@ -0,0 +1,4 @@
1
+ import { Request, Response, NextFunction } from 'express';
2
+ import { BffConfig } from "../config.js";
3
+ export declare function securityHeaders(config: BffConfig): ((_: Request, res: Response, next: NextFunction) => void)[];
4
+ //# sourceMappingURL=security-headers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"security-headers.d.ts","sourceRoot":"","sources":["../../../src/middleware/security-headers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AACvD,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAEvC,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,QAcR,OAAO,OAAO,QAAQ,QAAQ,YAAY,aAkBlF"}
@@ -0,0 +1,31 @@
1
+ import crypto from "crypto";
2
+ import helmet from "helmet";
3
+ export function securityHeaders(config) {
4
+ const contentSecurityPolicy = config.contentSecurityPolicy;
5
+ if (contentSecurityPolicy?.directives) {
6
+ for (const [_, values] of Object.entries(contentSecurityPolicy.directives)) {
7
+ // @ts-ignore //TODO values her har type Iterable (som ikke har `entries()`), men er egentlig en array. Kan sikkert skrives om litt.
8
+ for (const [i, value] of values.entries()) {
9
+ if (value === '{nonce}') {
10
+ values[i] = (_, res) => `'nonce-${res.locals.cspNonce}'`;
11
+ }
12
+ }
13
+ }
14
+ }
15
+ const generateCspNonceMiddleware = (_, res, next) => {
16
+ res.locals.cspNonce = crypto.randomBytes(16).toString("hex");
17
+ next();
18
+ };
19
+ const helmetMiddleware = helmet({
20
+ strictTransportSecurity: {
21
+ maxAge: 31536000,
22
+ includeSubDomains: false,
23
+ preload: false,
24
+ },
25
+ contentSecurityPolicy: contentSecurityPolicy ?? false,
26
+ });
27
+ return [
28
+ generateCspNonceMiddleware,
29
+ helmetMiddleware
30
+ ];
31
+ }
@@ -2,7 +2,7 @@ import crypto from "crypto";
2
2
  import helmet from "helmet";
3
3
  export function securityHeaders(config) {
4
4
  const contentSecurityPolicy = config.contentSecurityPolicy;
5
- if (contentSecurityPolicy === null || contentSecurityPolicy === void 0 ? void 0 : contentSecurityPolicy.directives) {
5
+ if (contentSecurityPolicy?.directives) {
6
6
  for (const [_, values] of Object.entries(contentSecurityPolicy.directives)) {
7
7
  for (const [i, value] of values.entries()) {
8
8
  if (value === '{nonce}') {
@@ -21,7 +21,7 @@ export function securityHeaders(config) {
21
21
  includeSubDomains: false,
22
22
  preload: false,
23
23
  },
24
- contentSecurityPolicy: contentSecurityPolicy !== null && contentSecurityPolicy !== void 0 ? contentSecurityPolicy : false,
24
+ contentSecurityPolicy: contentSecurityPolicy ?? false,
25
25
  });
26
26
  return [
27
27
  generateCspNonceMiddleware,
@@ -0,0 +1,3 @@
1
+ export function dynamoDbSessionStore(config?: {}): dynamoDbStore.DynamoDBStore<Record<string, unknown>>;
2
+ import dynamoDbStore from "connect-dynamodb";
3
+ //# sourceMappingURL=dynamoDbSessionStore.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamoDbSessionStore.d.mts","sourceRoot":"","sources":["../../../../src/middleware/sessions/dynamoDbSessionStore.mjs"],"names":[],"mappings":"AA6BA,wGAcC;0BA1CyB,kBAAkB"}
@@ -0,0 +1,3 @@
1
+ import dynamoDbStore from "connect-dynamodb";
2
+ export declare function dynamoDbSessionStore(config?: {}): dynamoDbStore.DynamoDBStore<Record<string, unknown>>;
3
+ //# sourceMappingURL=dynamoDbSessionStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dynamoDbSessionStore.d.ts","sourceRoot":"","sources":["../../../../src/middleware/sessions/dynamoDbSessionStore.ts"],"names":[],"mappings":"AACA,OAAO,aAAqC,MAAM,kBAAkB,CAAC;AA6BrE,wBAAgB,oBAAoB,CAAC,MAAM,KAAK,wDAc/C"}
@@ -0,0 +1,41 @@
1
+ import { DeleteItemCommand, DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
2
+ import dynamoDbStore from "connect-dynamodb";
3
+ import session from "express-session";
4
+ import { redact } from "../../utils.js";
5
+ const destroyByIdpSid = (config, client) => {
6
+ return async (idpSid) => {
7
+ console.log(`Front channel logout: deleting session(s) with idp-sid=${redact(idpSid)}`);
8
+ const query = new QueryCommand({
9
+ TableName: config['table'],
10
+ IndexName: "idp-sid-index",
11
+ ExpressionAttributeValues: { ":sid": { S: idpSid } },
12
+ ExpressionAttributeNames: { "#k": "idp-sid" },
13
+ KeyConditionExpression: "#k = :sid",
14
+ ProjectionExpression: "id"
15
+ });
16
+ const res = await client.send(query);
17
+ await Promise.all(res.Items.map((item) => {
18
+ console.log(`Front channel logout: deleting session ${redact(item.id?.S, 10)}`);
19
+ return client.send(new DeleteItemCommand({
20
+ TableName: config['table'],
21
+ Key: { id: item.id }
22
+ }));
23
+ }));
24
+ console.log(`Front channel logout: completed. ${res.Count} session(s) deleted`);
25
+ };
26
+ };
27
+ export function dynamoDbSessionStore(config = {}) {
28
+ const client = new DynamoDBClient({});
29
+ const DynamoDbStore = dynamoDbStore(session);
30
+ const sessionStoreConfig = {
31
+ ...config,
32
+ client,
33
+ specialKeys: [
34
+ { name: "idp-sid", type: "S" }
35
+ ],
36
+ skipThrowMissingSpecialKeys: true
37
+ };
38
+ const sessionStore = new DynamoDbStore(sessionStoreConfig);
39
+ sessionStore.destroyByIdpSid = destroyByIdpSid(config, client);
40
+ return sessionStore;
41
+ }
@@ -1,18 +1,9 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { DeleteItemCommand, DynamoDBClient, QueryCommand } from "@aws-sdk/client-dynamodb";
11
2
  import dynamoDbStore from "connect-dynamodb";
12
3
  import session from "express-session";
13
4
  import { redact } from "../../utils.js";
14
5
  const destroyByIdpSid = (config, client) => {
15
- return (idpSid) => __awaiter(void 0, void 0, void 0, function* () {
6
+ return async (idpSid) => {
16
7
  console.log(`Front channel logout: deleting session(s) with idp-sid=${redact(idpSid)}`);
17
8
  const query = new QueryCommand({
18
9
  TableName: config.table,
@@ -22,24 +13,28 @@ const destroyByIdpSid = (config, client) => {
22
13
  KeyConditionExpression: "#k = :sid",
23
14
  ProjectionExpression: "id"
24
15
  });
25
- const res = yield client.send(query);
26
- yield Promise.all(res.Items.map((item) => {
27
- var _a;
28
- console.log(`Front channel logout: deleting session ${redact((_a = item.id) === null || _a === void 0 ? void 0 : _a.S, 10)}`);
16
+ const res = await client.send(query);
17
+ await Promise.all(res.Items.map((item) => {
18
+ console.log(`Front channel logout: deleting session ${redact(item.id?.S, 10)}`);
29
19
  return client.send(new DeleteItemCommand({
30
20
  TableName: config.table,
31
21
  Key: { id: item.id }
32
22
  }));
33
23
  }));
34
24
  console.log(`Front channel logout: completed. ${res.Count} session(s) deleted`);
35
- });
25
+ };
36
26
  };
37
27
  export function dynamoDbSessionStore(config = {}) {
38
28
  const client = new DynamoDBClient({});
39
29
  const DynamoDbStore = dynamoDbStore({ session });
40
- const sessionStoreConfig = Object.assign(Object.assign({}, config), { client, specialKeys: [
30
+ const sessionStoreConfig = {
31
+ ...config,
32
+ client,
33
+ specialKeys: [
41
34
  { name: "idp-sid", type: "S" }
42
- ], skipThrowMissingSpecialKeys: true });
35
+ ],
36
+ skipThrowMissingSpecialKeys: true
37
+ };
43
38
  const sessionStore = new DynamoDbStore(sessionStoreConfig);
44
39
  sessionStore.destroyByIdpSid = destroyByIdpSid(config, client);
45
40
  return sessionStore;
@@ -0,0 +1,3 @@
1
+ export function memorySessionStore(config?: {}): session.MemoryStore;
2
+ import session from "express-session";
3
+ //# sourceMappingURL=memorySessionStore.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorySessionStore.d.mts","sourceRoot":"","sources":["../../../../src/middleware/sessions/memorySessionStore.mjs"],"names":[],"mappings":"AAQA,qEAIC;oBAZmB,iBAAiB"}
@@ -0,0 +1,3 @@
1
+ import session from "express-session";
2
+ export declare function memorySessionStore(config?: object): session.Store;
3
+ //# sourceMappingURL=memorySessionStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memorySessionStore.d.ts","sourceRoot":"","sources":["../../../../src/middleware/sessions/memorySessionStore.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,iBAAiB,CAAC;AAQtC,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,MAAW,iBAIrD"}
@@ -0,0 +1,11 @@
1
+ import session from "express-session";
2
+ import { redact } from "../../utils.js";
3
+ const destroyByIdpSid = async (idpSid) => {
4
+ // This is not supposed to be used outside localhost, so it is not implemented
5
+ console.log(`Pretending to destroyByIdpSid. idp-sid=${redact(idpSid)}`);
6
+ };
7
+ export function memorySessionStore(config = {}) {
8
+ const sessionStore = new session.MemoryStore(config);
9
+ sessionStore.destroyByIdpSid = destroyByIdpSid;
10
+ return sessionStore;
11
+ }
@@ -0,0 +1,2 @@
1
+ export function sessions(config: any): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>[];
2
+ //# sourceMappingURL=sessions.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.mts","sourceRoot":"","sources":["../../../../src/middleware/sessions/sessions.mjs"],"names":[],"mappings":"AAIA,sLAiCC"}
@@ -0,0 +1,3 @@
1
+ import { BffConfig } from "../../config.js";
2
+ export declare function sessions(config: BffConfig): import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>[];
3
+ //# sourceMappingURL=sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.d.ts","sourceRoot":"","sources":["../../../../src/middleware/sessions/sessions.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,SAAS,EAAC,MAAM,iBAAiB,CAAC;AAG1C,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,kJAiCzC"}
@@ -0,0 +1,39 @@
1
+ import session from "express-session";
2
+ import { dynamoDbSessionStore } from "./dynamoDbSessionStore.js";
3
+ import { memorySessionStore } from "./memorySessionStore.js";
4
+ export function sessions(config) {
5
+ let sessionStore;
6
+ if (config.sessionStoreType === 'memory') {
7
+ const sessionStoreOptions = config.sessionStoreOptions ?? {};
8
+ sessionStore = memorySessionStore(sessionStoreOptions);
9
+ }
10
+ else if (config.sessionStoreType === 'dynamodb') {
11
+ const sessionStoreOptions = config.sessionStoreOptions ?? {};
12
+ sessionStore = dynamoDbSessionStore(sessionStoreOptions);
13
+ }
14
+ else if (config.sessionStoreType) {
15
+ throw Error(`unknown sessionStoreType ${config.sessionStoreType}`);
16
+ }
17
+ else {
18
+ throw Error('missing sessionStoreType');
19
+ }
20
+ return [
21
+ session({
22
+ secret: config.sessionSecret,
23
+ store: sessionStore,
24
+ resave: false,
25
+ saveUninitialized: false,
26
+ cookie: config.cookie || {
27
+ httpOnly: true,
28
+ path: config.cookiePath,
29
+ secure: config.cookieSecure,
30
+ sameSite: config.cookieSameSite
31
+ },
32
+ }),
33
+ (req, _, next) => {
34
+ // make this function available to request handlers
35
+ req.destroySessionByIdpSid = sessionStore?.destroyByIdpSid;
36
+ next();
37
+ }
38
+ ];
39
+ }
@@ -2,14 +2,13 @@ import session from "express-session";
2
2
  import { dynamoDbSessionStore } from "./dynamoDbSessionStore.mjs";
3
3
  import { memorySessionStore } from "./memorySessionStore.mjs";
4
4
  export function sessions(config) {
5
- var _a, _b;
6
5
  let sessionStore;
7
6
  if (config.sessionStoreType === 'memory') {
8
- const sessionStoreOptions = (_a = config.sessionStoreOptions) !== null && _a !== void 0 ? _a : {};
7
+ const sessionStoreOptions = config.sessionStoreOptions ?? {};
9
8
  sessionStore = memorySessionStore(sessionStoreOptions);
10
9
  }
11
10
  else if (config.sessionStoreType === 'dynamodb') {
12
- const sessionStoreOptions = (_b = config.sessionStoreOptions) !== null && _b !== void 0 ? _b : {};
11
+ const sessionStoreOptions = config.sessionStoreOptions ?? {};
13
12
  sessionStore = dynamoDbSessionStore(sessionStoreOptions);
14
13
  }
15
14
  else if (config.sessionStoreType) {
@@ -33,7 +32,7 @@ export function sessions(config) {
33
32
  }),
34
33
  (req, _, next) => {
35
34
  // make this function available to request handlers
36
- req.destroySessionByIdpSid = sessionStore === null || sessionStore === void 0 ? void 0 : sessionStore.destroyByIdpSid;
35
+ req.destroySessionByIdpSid = sessionStore?.destroyByIdpSid;
37
36
  next();
38
37
  }
39
38
  ];
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-routes.d.mts","sourceRoot":"","sources":["../../../src/middleware/static-routes.mjs"],"names":[],"mappings":"AAIA,+CAiBC"}
@@ -0,0 +1,3 @@
1
+ import { BffConfig } from "../config.js";
2
+ export declare function staticRoutes(config: BffConfig): import("express-serve-static-core").Router;
3
+ //# sourceMappingURL=static-routes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"static-routes.d.ts","sourceRoot":"","sources":["../../../src/middleware/static-routes.ts"],"names":[],"mappings":"AAGA,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AAEvC,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,8CAiB7C"}