@oslokommune/auth-bff 2.0.0-beta1 → 2.0.0-beta3

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 (39) hide show
  1. package/README.md +344 -0
  2. package/dist/package.json +12 -11
  3. package/dist/src/config.d.ts +107 -4
  4. package/dist/src/config.d.ts.map +1 -1
  5. package/dist/src/config.js +7 -7
  6. package/dist/src/middleware/OidcMiddleware.d.ts.map +1 -1
  7. package/dist/src/middleware/OidcMiddleware.js +1 -0
  8. package/dist/src/middleware/oidc-routes.d.ts +3 -0
  9. package/dist/src/middleware/oidc-routes.d.ts.map +1 -0
  10. package/dist/src/middleware/oidc-routes.js +10 -0
  11. package/dist/src/middleware/proxy-routes.d.ts +4 -0
  12. package/dist/src/middleware/proxy-routes.d.ts.map +1 -0
  13. package/dist/src/middleware/proxy-routes.js +28 -0
  14. package/dist/src/middleware/proxy-routes.mjs +1 -1
  15. package/dist/src/middleware/security-headers.d.ts +4 -0
  16. package/dist/src/middleware/security-headers.d.ts.map +1 -0
  17. package/dist/src/middleware/security-headers.js +31 -0
  18. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.ts +3 -0
  19. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.ts.map +1 -0
  20. package/dist/src/middleware/sessions/dynamoDbSessionStore.js +41 -0
  21. package/dist/src/middleware/sessions/memorySessionStore.d.ts +3 -0
  22. package/dist/src/middleware/sessions/memorySessionStore.d.ts.map +1 -0
  23. package/dist/src/middleware/sessions/memorySessionStore.js +11 -0
  24. package/dist/src/middleware/sessions/sessions.d.ts +3 -0
  25. package/dist/src/middleware/sessions/sessions.d.ts.map +1 -0
  26. package/dist/src/middleware/sessions/sessions.js +39 -0
  27. package/dist/src/middleware/static-routes.d.ts +3 -0
  28. package/dist/src/middleware/static-routes.d.ts.map +1 -0
  29. package/dist/src/middleware/static-routes.js +19 -0
  30. package/dist/src/server.d.ts +3 -0
  31. package/dist/src/server.d.ts.map +1 -0
  32. package/dist/src/server.js +43 -0
  33. package/dist/src/server.mjs +0 -0
  34. package/dist/src/utils.d.ts +1 -1
  35. package/dist/src/utils.d.ts.map +1 -1
  36. package/dist/src/vite-plugin.d.ts +5 -0
  37. package/dist/src/vite-plugin.d.ts.map +1 -0
  38. package/dist/src/vite-plugin.js +26 -0
  39. package/package.json +12 -11
package/README.md ADDED
@@ -0,0 +1,344 @@
1
+ # auth-bff
2
+
3
+ A NodeJS Backend for frontend.
4
+
5
+ Features:
6
+
7
+ * Two "modes" of operation
8
+ * A vite plugin for use during development
9
+ * A standalone mode for use in production (e.g. inside a docker container)
10
+ * Supports generic OIDC auth code flow clients
11
+ * Has special support for `okdata`-generated Idporten clients
12
+ * Handles login/logout and sessions (using DynamoDb as a store)
13
+ * Proxies API calls
14
+ * Serves a static web app
15
+ * Includes simple React components for handling login-state
16
+
17
+ See https://github.com/oslokommune/auth-bff-example for an example React app using this package.
18
+
19
+ ## Getting started
20
+
21
+ ### Dev mode
22
+
23
+ 1. Install package
24
+
25
+ ```shell
26
+ npm install @oslokommune/auth-bff
27
+ ```
28
+
29
+ 2. Add plugin to your vite config
30
+
31
+ ```ts
32
+ import {defineConfig} from 'vite'
33
+ import bff from '@oslokommune/auth-bff/vite-plugin'
34
+
35
+ export default defineConfig({
36
+ plugins: [
37
+ bff()
38
+ ],
39
+ ...
40
+ })
41
+ ```
42
+
43
+ 3. Create a [config file](#configuration)
44
+
45
+ 4. Run!
46
+
47
+ ### Standalone mode
48
+
49
+ 1. Install package globally
50
+
51
+ ```shell
52
+ npm install -g @oslokommune/auth-bff
53
+ ```
54
+
55
+ 2. Create a [config file](#configuration)
56
+
57
+ 3. Run with
58
+
59
+ ```shell
60
+ auth-bff
61
+ ```
62
+
63
+ ## Running in Docker
64
+
65
+ When running in docker you should specify the version to use, and make sure it matches the one used in package.json.
66
+
67
+ Example dockerfile:
68
+ ```dockerfile
69
+ FROM node:23-alpine AS base
70
+
71
+ FROM base AS react-build
72
+ WORKDIR /home/react
73
+ COPY package*.json /home/react
74
+ RUN npm install
75
+ COPY . ./
76
+ RUN npm run build
77
+
78
+ FROM base
79
+ WORKDIR /application
80
+ EXPOSE 8080
81
+ COPY --from=react-build /home/react/dist /application/dist
82
+ ENV NODE_ENV=production
83
+ RUN npm install -g @oslokommune/auth-bff@2.0.0-beta3
84
+ COPY bff.config.json /application/
85
+ CMD ["auth-bff"]
86
+ ```
87
+
88
+ To use different configuration for different environments, you can create separate config files for each and select it at build time (using build args).
89
+ For example, with `bff.config.dev.json` and `bff.config.prod.json`:
90
+
91
+ ```dockerfile
92
+ ARG ENVIRONMENT=''
93
+ COPY bff.config.${ENVIRONMENT}.json /application/bff.config.json
94
+ CMD ["auth-bff"]
95
+ ```
96
+
97
+ Or select it at runtime, using an env var:
98
+
99
+ ```dockerfile
100
+ COPY bff.config*.json /application/
101
+ CMD exec auth-bff --configFile bff.config.${ENVIRONMENT}.json
102
+ ```
103
+
104
+
105
+ ## Configuration
106
+
107
+ Configuration is defined in json-files that look like this:
108
+
109
+ ```json
110
+ {
111
+ "issuer": "https://example-issuer.com/",
112
+ "clientId": "example-client",
113
+ "clientSecret": "{ssm:/secret/from/parameter/store}",
114
+ "redirectUri": "http://localhost:3000/auth/callback",
115
+ "cookieSecure": false,
116
+ "cookieSameSite": false,
117
+ "postLogoutRedirectUri": "http://localhost:3000/",
118
+ "sessionSecret": "{env:SECRET_FROM_ENV}",
119
+ "sessionStoreType": "memory",
120
+ "proxyTargets": {
121
+ "/api": "http://localhost:8080/api"
122
+ }
123
+ }
124
+ ```
125
+
126
+ By default it looks for a file named `bff.config.json`, but this can be overridden:
127
+
128
+ Vite:
129
+
130
+ ```ts
131
+ plugins: [
132
+ bff({configFile: ['bff.config.local.json', 'bff.config.json']}) //First existing file is used
133
+ ]
134
+ ```
135
+
136
+ Standalone:
137
+
138
+ ```shell
139
+ auth-bff --configFile /path/to/bff.config.json
140
+ ```
141
+
142
+ The config file supports two special forms for loading values from other sources. Primarily meant for loading secrets:
143
+
144
+ Environment values:
145
+
146
+ ```json
147
+ {
148
+ "sessionSecret": "{env:MY_SECRET}"
149
+ }
150
+ ```
151
+
152
+ AWS Parameter store:
153
+
154
+ ```json
155
+ {
156
+ "sessionSecret": "{ssm:/name/of/parameter}"
157
+ }
158
+ ```
159
+
160
+ This loads from the configured AWS environment. For this to work on your local machine the `AWS_PROFILE` environment
161
+ variable must be set, and you must be signed in to that profile
162
+
163
+ ℹ️ [See `config.ts` for a description of all config parameters](src/config.ts)
164
+
165
+ ## Using with ID-porten (via `okdata`):
166
+
167
+ `auth-bff` Has special support for keys generated by [`okdata`](https://github.com/oslokommune/okdata-cli).
168
+
169
+ 1. Start by creating a new client and key using okdata:
170
+
171
+ ```shell
172
+ ~> okdata pubs create-client
173
+ * Environment test
174
+ * Team <team>
175
+ * Client type ID-porten
176
+ * Integration name (identifying in which system or case this client will be used) <your name here>
177
+ * Redirect URIs (comma-separated) http://localhost:3000/auth/callback
178
+ * Post logout Redirect URIs (comma-separated) http://localhost:3000
179
+ * Frontchannel logout URI http://localhost:3000/auth/logout
180
+ * Client URI (back URI) http://localhost:3000
181
+ ...
182
+ ~> okdata pubs create-key
183
+ * Environment test
184
+ * Client <same client as above>
185
+ * Where should the key be stored? Send the key to your AWS Parameter Store
186
+ * AWS account number <accno>
187
+ * AWS region eu-west-1 (Ireland)
188
+ * Automatic key rotation will replace the key in SSM nightly on weekdays.
189
+ Enable automatic key rotation for this client? Yes
190
+ ...
191
+ A new key has been created and the following parameters have been written to SSM:
192
+ - /okdata/maskinporten/11111111-2222-3333-4444-555555555555/key.json
193
+ ...
194
+ ```
195
+
196
+ 2. Set the following options in the config file:
197
+
198
+ ```json
199
+ {
200
+ "issuer": "https://test.idporten.no/",
201
+ "clientId": "11111111-2222-3333-4444-555555555555",
202
+ "okDataIdPortenKeyName": "/okdata/maskinporten/11111111-2222-3333-4444-555555555555/key.json",
203
+ ...
204
+ }
205
+ ```
206
+
207
+ 3. Done!
208
+
209
+ ## Configuring session storage
210
+
211
+ Currently only dynamoDb is supported for storing sessions in production. It requires some setup.
212
+ To work properly, the table must have ttl enabled, and an extra index for the `idp-sid`-property (used to delete
213
+ sessions during front-channel logout)
214
+
215
+ > [!WARNING]
216
+ > If the table does not exist, it will be automatically created with settings not appropriate for production.
217
+
218
+
219
+ Here is an example configuration in terraform
220
+
221
+ ```terraform
222
+ resource "aws_dynamodb_table" "session_dynamodb_table" {
223
+ name = "${local.environment}-${local.main_container_name}-sessions"
224
+ billing_mode = "PAY_PER_REQUEST"
225
+ hash_key = "id"
226
+
227
+ on_demand_throughput {
228
+ max_read_request_units = 20
229
+ max_write_request_units = 20
230
+ }
231
+
232
+ attribute {
233
+ name = "id"
234
+ type = "S"
235
+ }
236
+
237
+ attribute {
238
+ name = "idp-sid"
239
+ type = "S"
240
+ }
241
+
242
+ ttl {
243
+ enabled = true
244
+ attribute_name = "expires"
245
+ }
246
+
247
+ global_secondary_index {
248
+ hash_key = "idp-sid"
249
+ name = "idp-sid-index"
250
+ projection_type = "KEYS_ONLY"
251
+ on_demand_throughput {
252
+ max_read_request_units = 20
253
+ max_write_request_units = 20
254
+ }
255
+ }
256
+ }
257
+ ```
258
+
259
+ Then remember to update the config to use the new table:
260
+
261
+ ```json
262
+ {
263
+ "sessionStoreOptions": {
264
+ "table": "myteam-dev-myservice-sessions"
265
+ }
266
+ }
267
+ ```
268
+
269
+ The read/write limits above are just an example, and can be changed/removed
270
+
271
+ ## Required AWS permissions
272
+
273
+ When using SSM parameters or `okDataIdPortenKeyName` in the config, the service will need the `ssm:GetParameter`
274
+ permission for each parameter
275
+
276
+ If using dynamodb, the service will need the following permissions for the table
277
+
278
+ ```
279
+ dynamodb:CreateTable
280
+ dynamodb:DescribeTable
281
+ dynamodb:PutItem
282
+ dynamodb:DeleteItem
283
+ dynamodb:GetItem
284
+ dynamodb:Scan
285
+ dynamodb:UpdateItem
286
+ ```
287
+
288
+ ## React component
289
+
290
+ This package also includes a React component for handling authentication state. It will redirect to login if required
291
+ and optionally automatically poll for changes to authentication state.
292
+
293
+ ### AuthContextProvider
294
+
295
+ ```tsx
296
+ import {AuthContextProvider} from "@oslokommune/auth-bff/react";
297
+ import {PktLoader} from "@oslokommune/punkt-react";
298
+
299
+ const fiveMinutes = 5 * 60 * 1000;
300
+
301
+ <AuthContextProvider authRequired={true} loaderComponent={<PktLoader/>} pollInterval={fiveMinues}>
302
+ <App/>
303
+ </AuthContextProvider>
304
+ ```
305
+
306
+ | Option | Description |
307
+ |----------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
308
+ | authRequired | Whether authentication is required. If true, will redirect to login before rendering child components (default: true) |
309
+ | loaderComponent | React component to display while loading auth state. (default: null) |
310
+ | baseUrl | Must be set to the same baseUrl as in the json config for login/logout to work correctly (default: '') |
311
+ | pollInterval | Minimum interval in milliseconds between checks if session is still active. Will set authState to 'expired' if session is expired (default: disabled) |
312
+
313
+
314
+ ### useAuthContext
315
+
316
+ Hook to get current AuthState. Must be called in a component inside the AuthContextProvider.
317
+ ```tsx
318
+ import {useAuthContext} from "@oslokommune/auth-bff/react";
319
+
320
+ const {user, authState, login} = useAuthContext()
321
+ if(authState === 'authenticated') {
322
+ console.log(`Hello, ${user.pid}`)
323
+ } else {
324
+ login()
325
+ }
326
+
327
+ ```
328
+
329
+ | Property | Description |
330
+ |-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|
331
+ | user | The currently logged in user, or null if not logged in. User object contains the id_token claims returned from the usr endpoint. See config option `userClaims`. |
332
+ | login | Function that will redirect to the login endpoint when invoked |
333
+ | logout | Function that will redirect to the logout endpoint when invoked |
334
+ | authState | Current auth state. See table below for values | | |
335
+
336
+ #### AuthState
337
+ | Value | Description |
338
+ |-----------------|------------------------------------------------------------------------------------------------------------------|
339
+ | pending | Initial value before auth state has been determined |
340
+ | authenticated | User is authenticated. |
341
+ | unauthenticated | User is not authenticated |
342
+ | expired | User was authenticated, but the session has expired. Can be used to display message to user or redirect to login | | |
343
+ | error | Failed to determine auth state | | |
344
+
package/dist/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@oslokommune/auth-bff",
3
- "version": "2.0.0-beta1",
3
+ "version": "2.0.0-beta3",
4
4
  "repository": "https://github.com/oslokommune/auth-bff.git",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "tsc",
10
- "run": "node ./dist/server.mjs",
10
+ "run": "node ./dist/src/server.js",
11
11
  "build-and-publish": "tsc && npm publish",
12
12
  "build-and-publish-prerelease": "tsc && npm publish --tag prerelease",
13
13
  "test": "vitest"
14
14
  },
15
15
  "exports": {
16
- "./vite-plugin": "./dist/src/vite-plugin.mjs",
16
+ "./vite-plugin": "./dist/src/vite-plugin.js",
17
17
  "./react": "./dist/src/react/index.js"
18
18
  },
19
19
  "bin": {
20
- "auth-bff": "dist/src/server.mjs"
20
+ "auth-bff": "dist/src/server.js"
21
21
  },
22
22
  "files": [
23
23
  "/dist"
@@ -27,6 +27,7 @@
27
27
  "license": "",
28
28
  "description": "",
29
29
  "devDependencies": {
30
+ "@types/command-line-args": "^5.2.3",
30
31
  "@types/compression": "^1.8.1",
31
32
  "@types/express": "^4.17.22",
32
33
  "@types/express-session": "^1.18.2",
@@ -39,19 +40,19 @@
39
40
  "vitest": "^4.0.18"
40
41
  },
41
42
  "dependencies": {
42
- "@aws-sdk/client-dynamodb": "^3.817.0",
43
- "@aws-sdk/client-ssm": "^3.817.0",
43
+ "@aws-sdk/client-dynamodb": "^3.990.0",
44
+ "@aws-sdk/client-ssm": "^3.990.0",
44
45
  "command-line-args": "^6.0.1",
45
- "compression": "^1.8.0",
46
+ "compression": "^1.8.1",
46
47
  "connect-dynamodb": "^3.0.5",
47
- "express": "4.21.2",
48
- "express-session": "1.18.2",
48
+ "express": "4.22.1",
49
+ "express-session": "1.19.0",
49
50
  "find-up": "^7.0.0",
50
51
  "helmet": "^8.1.0",
51
52
  "http-proxy-middleware": "^3.0.5",
52
- "jose": "^6.0.11",
53
+ "jose": "^6.1.3",
53
54
  "node-forge": "1.3.3",
54
- "openid-client": "^6.8.1",
55
+ "openid-client": "^6.8.2",
55
56
  "string-replace-middleware": "^1.1.0"
56
57
  }
57
58
  }
@@ -1,23 +1,126 @@
1
+ import { HelmetOptions } from "helmet";
2
+ import session from "express-session";
1
3
  export type BffConfig = {
4
+ /**
5
+ * The port at which the app will be served. Only used in standalone mode, to change the port used during development,
6
+ * set it in your vite config instead.
7
+ */
8
+ port: number;
9
+ /**
10
+ * The base root path. Change if app is served from a non-root path.
11
+ *
12
+ * Default: `/`
13
+ */
2
14
  basePath?: string;
15
+ /**
16
+ * The root path of the static resources to be served
17
+ *
18
+ * Default: `/dist`
19
+ */
3
20
  staticRootPath?: string;
21
+ /**
22
+ * The issuer
23
+ *
24
+ * Example: `https://test.idporten.no/`
25
+ */
4
26
  issuer: string;
27
+ /**
28
+ * The ID of the client
29
+ */
5
30
  clientId: string;
31
+ /**
32
+ * The client secret. Not used if `okDataIdPortenKeyName` is set.
33
+ */
6
34
  clientSecret?: string;
35
+ /**
36
+ * The redirect uri configured for the client
37
+ */
7
38
  redirectUri: string;
8
- resources: Array<string>;
39
+ /**
40
+ * The intended audience for the tokens. Value(s) set here can be used to verify audience on the recipient end.
41
+ * See https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-indicators-05.
42
+ *
43
+ * Example: `["https://example.com/api1", "https://example.com/api2"]`
44
+ */
45
+ resources?: Array<string>;
46
+ /**
47
+ * express-session cookie options, note that if this is set, the below cookie*-options will have no effect
48
+ */
49
+ cookie?: session.CookieOptions;
50
+ /**
51
+ * Sets the Path attribute of the cookie. Should most likely be the same as basePath. See https://expressjs.com/en/resources/middleware/session.html
52
+ *
53
+ * Default: `/`
54
+ */
9
55
  cookiePath?: string;
10
- cookieSecure?: Boolean;
11
- cookieSameSite: Boolean | string;
56
+ /**
57
+ * Sets the Secure attribute of the cookie. See https://expressjs.com/en/resources/middleware/session.html
58
+ *
59
+ * Default: `true`
60
+ */
61
+ cookieSecure?: boolean;
62
+ /**
63
+ * Sets the SameSite attribute of the cookie. See https://expressjs.com/en/resources/middleware/session.html
64
+ *
65
+ * Default: `"lax"`
66
+ */
67
+ cookieSameSite: boolean | "lax" | "none" | "strict";
68
+ /**
69
+ * The post logout redirect uri configured for the client
70
+ */
12
71
  postLogoutRedirectUri: string;
72
+ /**
73
+ * The name of the key that okdata stored in parameter store.
74
+ *
75
+ * Example: `/okdata/maskinporten/11111111-2222-3333-4444-555555555555/key.json`
76
+ */
13
77
  okDataIdPortenKeyName: string;
78
+ /**
79
+ * Secret used to sign sessions
80
+ */
14
81
  sessionSecret: string;
82
+ /**
83
+ * The type of session store used. Only `memory` and `dynamodb` currently supported. `memory` is only for dev use
84
+ */
15
85
  sessionStoreType: 'memory' | 'dynamodb';
86
+ /**
87
+ * Options that will be passed to the chosen sessionStore. Options depend on the type of store chosen
88
+ *
89
+ * Example: `{"table": "my-custom-dynamodb-table"}`
90
+ */
16
91
  sessionStoreOptions?: object;
92
+ /**
93
+ * Map of paths and remote targets that will be forwarded by the proxy
94
+ *
95
+ * Example: `{'/api': 'http://example.com/api'}`
96
+ */
17
97
  proxyTargets: {
18
98
  [path: string]: string;
19
99
  };
20
- userClaims: Array<string>;
100
+ /**
101
+ * List of claims in the id_token that are returned by the /user-endpoint. By default all are returned
102
+ *
103
+ * Example: `["pid"]`
104
+ */
105
+ userClaims?: Array<string>;
106
+ /**
107
+ * Content security policy configuration passed to helmet.
108
+ * See https://github.com/helmetjs/helmet for details. Note that since the config in limited to json,
109
+ * some features are not supported. To set a nonce value, use the special value `"{nonce}"` instead.
110
+ *
111
+ *
112
+ * Example:
113
+ * ```json
114
+ * {
115
+ * "directives": {
116
+ * "default-src": ["'self'", "https://*.oslo.kommune.no", "https://*.oslo.systems"],
117
+ * "script-src": ["'self'", "{nonce}"],
118
+ * ...
119
+ * }
120
+ * }
121
+ * ```
122
+ */
123
+ contentSecurityPolicy?: Exclude<HelmetOptions['contentSecurityPolicy'], Boolean>;
21
124
  };
22
125
  export declare function getEnv(env: string, defaultVal?: string, parseFn?: (val: string) => string): string;
23
126
  export declare function getSsmParameter(name: string, withDecryption?: boolean): Promise<string>;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,SAAS,GAAG;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACxB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,cAAc,EAAE,OAAO,GAAG,MAAM,CAAA;IAChC,qBAAqB,EAAE,MAAM,CAAA;IAC7B,qBAAqB,EAAE,MAAM,CAAA;IAC7B,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,QAAQ,GAAG,UAAU,CAAA;IACvC,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,YAAY,EAAE;QAAC,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAC,CAAA;IACtC,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;CAC1B,CAAA;AAED,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,UAQzF;AAGD,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,GAAE,OAAc,mBAMjF;AAWD,wBAAsB,UAAU,CAAC,UAAU,GAAE,MAA0B,sBAyBtE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,QAAQ,CAAC;AACrC,OAAO,OAAO,MAAM,iBAAiB,CAAC;AAEtC,MAAM,MAAM,SAAS,GAAG;IACtB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAA;IAChB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC,aAAa,CAAA;IAC9B;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;;OAIG;IACH,cAAc,EAAE,OAAO,GAAG,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAA;IACnD;;OAEG;IACH,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;;;OAIG;IACH,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAA;IACrB;;OAEG;IACH,gBAAgB,EAAE,QAAQ,GAAG,UAAU,CAAA;IACvC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B;;;;OAIG;IACH,YAAY,EAAE;QAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAA;IACxC;;;;OAIG;IACH,UAAU,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAA;IAC1B;;;;;;;;;;;;;;;;OAgBG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC,CAAA;CACjF,CAAA;AAUD,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,UAQzF;AAID,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,cAAc,GAAE,OAAc,mBAMjF;AAKD,wBAAsB,UAAU,CAAC,UAAU,GAAE,MAA0B,sBAyBtE"}
@@ -1,5 +1,12 @@
1
1
  import { findUp } from 'find-up';
2
2
  import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
3
+ const defaultConfig = {
4
+ basePath: "",
5
+ cookiePath: '/',
6
+ cookieSecure: true,
7
+ cookieSameSite: 'lax',
8
+ staticRootPath: './dist'
9
+ };
3
10
  export function getEnv(env, defaultVal, parseFn) {
4
11
  if (process.env[env]) {
5
12
  return parseFn ? parseFn(process.env[env]) : process.env[env];
@@ -19,13 +26,6 @@ export async function getSsmParameter(name, withDecryption = true) {
19
26
  WithDecryption: withDecryption
20
27
  })).then(p => p.Parameter.Value);
21
28
  }
22
- const defaultConfig = {
23
- basePath: "",
24
- cookiePath: '/',
25
- cookieSecure: true,
26
- cookieSameSite: 'lax',
27
- staticRootPath: './dist'
28
- };
29
29
  let config;
30
30
  export async function loadConfig(configFile = 'bff.config.json') {
31
31
  if (config)
@@ -1 +1 @@
1
- {"version":3,"file":"OidcMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/OidcMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,mBAAmB,EAAC,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAK5D,qBAAa,cAAc;;IAKzB;;;;OAIG;gBACS,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB;WASpD,MAAM,CAAC,MAAM,EAAE,SAAS;IAgErC,IAAI,gBAAgB,KACV,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAWxD;IAED,IAAI,KAAK,KACO,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBA+B9D;IAqBD,IAAI,QAAQ,KACI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBAuC9D;IAED,IAAI,IAAI,KACQ,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,iDAa9D;IAED,IAAI,MAAM,KACA,KAAK,OAAO,EAAE,KAAK,QAAQ,UASpC;IAED,IAAI,kBAAkB,KACN,KAAK,OAAO,EAAE,KAAK,QAAQ,mBAc1C;CACF"}
1
+ {"version":3,"file":"OidcMiddleware.d.ts","sourceRoot":"","sources":["../../../src/middleware/OidcMiddleware.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,mBAAmB,EAAC,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,KAAK,EAAC,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAC,MAAM,SAAS,CAAA;AAK5D,qBAAa,cAAc;;IAKzB;;;;OAIG;gBACS,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,mBAAmB;WASpD,MAAM,CAAC,MAAM,EAAE,SAAS;IAgErC,IAAI,gBAAgB,KACV,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,UAWxD;IAED,IAAI,KAAK,KACO,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBA+B9D;IAqBD,IAAI,QAAQ,KACI,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,mBAuC9D;IAED,IAAI,IAAI,KACQ,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,iDAc9D;IAED,IAAI,MAAM,KACA,KAAK,OAAO,EAAE,KAAK,QAAQ,UASpC;IAED,IAAI,kBAAkB,KACN,KAAK,OAAO,EAAE,KAAK,QAAQ,mBAc1C;CACF"}
@@ -121,6 +121,7 @@ export class OidcMiddleware {
121
121
  try {
122
122
  const tokenResponse = await __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getFreshTokens).call(this, req);
123
123
  if (!tokenResponse) {
124
+ console.log('/user 401: No tokenset');
124
125
  return res.sendStatus(401);
125
126
  }
126
127
  return res.send(req.session.userClaims);
@@ -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,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
+ }
@@ -18,7 +18,7 @@ export function proxyRoutes(config, oidcMiddleware) {
18
18
  proxyReq.removeHeader("Cookie");
19
19
  },
20
20
  proxyRes: (proxyRes, req, res) => {
21
- console.log(`Proxied ${req.originalUrl} -> ${target}${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,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
+ }
@@ -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
+ }
@@ -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,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
+ }
@@ -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"}
@@ -0,0 +1,19 @@
1
+ import express from "express";
2
+ import { stringReplace } from "string-replace-middleware";
3
+ import path from "path";
4
+ export function staticRoutes(config) {
5
+ const router = express.Router();
6
+ router.use(stringReplace({
7
+ '__CSP_NONCE__': (_, res) => res.locals.cspNonce
8
+ }, {
9
+ contentTypeFilterRegexp: /^text\/html/
10
+ }));
11
+ const staticPath = path.resolve(process.cwd(), config.staticRootPath);
12
+ console.log(`Serving static content from '${staticPath}'`);
13
+ router.use(express.static(staticPath, { index: false }));
14
+ router.get('*', function (_, res) {
15
+ res.set('Cache-Control', 'no-store');
16
+ res.sendFile(path.resolve(staticPath, 'index.html'));
17
+ });
18
+ return router;
19
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,43 @@
1
+ #!/usr/bin/env node
2
+ import express from "express";
3
+ import compression from "compression";
4
+ import { loadConfig } from './config.js';
5
+ import { proxyRoutes } from "./middleware/proxy-routes.js";
6
+ import { staticRoutes } from "./middleware/static-routes.js";
7
+ import { securityHeaders } from "./middleware/security-headers.js";
8
+ import { sessions } from "./middleware/sessions/sessions.js";
9
+ import { oidcRoutes } from "./middleware/oidc-routes.js";
10
+ import { OidcMiddleware } from "./middleware/OidcMiddleware.js";
11
+ import commandLineArgs from "command-line-args";
12
+ import packageJson from "../package.json" with { type: 'json' };
13
+ const options = commandLineArgs([{ name: 'configFile' }]);
14
+ const config = await loadConfig(options.configFile);
15
+ const port = process.env.port || config.port || 8080;
16
+ const oidcMiddleware = await OidcMiddleware.create(config);
17
+ const requestLogger = (req, _, next) => {
18
+ next();
19
+ console.log(`${req.method} ${req.originalUrl}, referer=${req.get('Referer')}, user-agent=${req.get('User-Agent')}`);
20
+ };
21
+ const app = express();
22
+ app.set('trust proxy', true); // TODO: sjekk om denne kan/bør være strengere: https://expressjs.com/en/api.html#trust.proxy.options.table
23
+ app.disable("x-powered-by");
24
+ app.use(compression());
25
+ app.use(sessions(config));
26
+ app.use(securityHeaders(config));
27
+ app.get("/health", (_, res) => {
28
+ res.send("OK");
29
+ });
30
+ const basePath = config.basePath || "/";
31
+ app.use(basePath, oidcRoutes(oidcMiddleware));
32
+ app.use(requestLogger); //NB, må stå her for å ikke logge auth-requestene over
33
+ app.use(basePath, proxyRoutes(config, oidcMiddleware));
34
+ app.use(basePath, staticRoutes(config));
35
+ const server = app.listen(port, () => {
36
+ console.log(`auth-bff ${packageJson.version} started on port ${port}`);
37
+ });
38
+ process.on('SIGTERM', () => {
39
+ console.log('SIGTERM received. Closing...');
40
+ server.close(() => {
41
+ console.log('Server closed');
42
+ });
43
+ });
File without changes
@@ -1,2 +1,2 @@
1
- export function redact(string: any, length?: number): string;
1
+ export declare function redact(string: string, length?: number): string;
2
2
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.js"],"names":[],"mappings":"AAAA,6DAEC"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,UAExD"}
@@ -0,0 +1,5 @@
1
+ import { Plugin } from 'vite';
2
+ export default function bff({ configFile }?: {
3
+ configFile?: string;
4
+ }): Plugin;
5
+ //# sourceMappingURL=vite-plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vite-plugin.d.ts","sourceRoot":"","sources":["../../src/vite-plugin.ts"],"names":[],"mappings":"AAGA,OAAO,EAAgB,MAAM,EAAC,MAAM,MAAM,CAAA;AAsB1C,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAC,UAAU,EAAC,GAAE;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAM,GAAG,MAAM,CAO5E"}
@@ -0,0 +1,26 @@
1
+ import express from "express";
2
+ import { loadConfig } from "./config.js";
3
+ import { OidcMiddleware } from "./middleware/OidcMiddleware.js";
4
+ function configureServer(configFilePath) {
5
+ return async ({ middlewares }) => {
6
+ const { oidcRoutes } = await import("./middleware/oidc-routes.js");
7
+ const { proxyRoutes } = await import("./middleware/proxy-routes.js");
8
+ const { sessions } = await import("./middleware/sessions/sessions.js");
9
+ const config = await loadConfig(configFilePath);
10
+ const oidcMiddleware = await OidcMiddleware.create(config);
11
+ const basePath = config.basePath || "/";
12
+ const app = express();
13
+ app.use(sessions(config));
14
+ app.use(basePath, oidcRoutes(oidcMiddleware));
15
+ app.use(basePath, proxyRoutes(config, oidcMiddleware));
16
+ middlewares.use(app);
17
+ };
18
+ }
19
+ export default function bff({ configFile } = {}) {
20
+ return {
21
+ name: 'bff',
22
+ apply: 'serve',
23
+ configureServer: configureServer(configFile),
24
+ configurePreviewServer: configureServer(configFile)
25
+ };
26
+ }
package/package.json CHANGED
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "name": "@oslokommune/auth-bff",
3
- "version": "2.0.0-beta1",
3
+ "version": "2.0.0-beta3",
4
4
  "repository": "https://github.com/oslokommune/auth-bff.git",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
8
8
  "scripts": {
9
9
  "build": "tsc",
10
- "run": "node ./dist/server.mjs",
10
+ "run": "node ./dist/src/server.js",
11
11
  "build-and-publish": "tsc && npm publish",
12
12
  "build-and-publish-prerelease": "tsc && npm publish --tag prerelease",
13
13
  "test": "vitest"
14
14
  },
15
15
  "exports": {
16
- "./vite-plugin": "./dist/src/vite-plugin.mjs",
16
+ "./vite-plugin": "./dist/src/vite-plugin.js",
17
17
  "./react": "./dist/src/react/index.js"
18
18
  },
19
19
  "bin": {
20
- "auth-bff": "dist/src/server.mjs"
20
+ "auth-bff": "dist/src/server.js"
21
21
  },
22
22
  "files": [
23
23
  "/dist"
@@ -27,6 +27,7 @@
27
27
  "license": "",
28
28
  "description": "",
29
29
  "devDependencies": {
30
+ "@types/command-line-args": "^5.2.3",
30
31
  "@types/compression": "^1.8.1",
31
32
  "@types/express": "^4.17.22",
32
33
  "@types/express-session": "^1.18.2",
@@ -39,19 +40,19 @@
39
40
  "vitest": "^4.0.18"
40
41
  },
41
42
  "dependencies": {
42
- "@aws-sdk/client-dynamodb": "^3.817.0",
43
- "@aws-sdk/client-ssm": "^3.817.0",
43
+ "@aws-sdk/client-dynamodb": "^3.990.0",
44
+ "@aws-sdk/client-ssm": "^3.990.0",
44
45
  "command-line-args": "^6.0.1",
45
- "compression": "^1.8.0",
46
+ "compression": "^1.8.1",
46
47
  "connect-dynamodb": "^3.0.5",
47
- "express": "4.21.2",
48
- "express-session": "1.18.2",
48
+ "express": "4.22.1",
49
+ "express-session": "1.19.0",
49
50
  "find-up": "^7.0.0",
50
51
  "helmet": "^8.1.0",
51
52
  "http-proxy-middleware": "^3.0.5",
52
- "jose": "^6.0.11",
53
+ "jose": "^6.1.3",
53
54
  "node-forge": "1.3.3",
54
- "openid-client": "^6.8.1",
55
+ "openid-client": "^6.8.2",
55
56
  "string-replace-middleware": "^1.1.0"
56
57
  }
57
58
  }