@oslokommune/auth-bff 2.0.0-beta1 → 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 (39) hide show
  1. package/README.md +240 -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 +10 -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,240 @@
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
+ ## Configuration
64
+
65
+ Configuration is set in json-files that look like this:
66
+
67
+ ```json
68
+ {
69
+ "issuer": "https://example-issuer.com/",
70
+ "clientId": "example-client",
71
+ "clientSecret": "{ssm:/secret/from/parameter/store}",
72
+ "redirectUri": "http://localhost:3000/auth/callback",
73
+ "cookieSecure": false,
74
+ "cookieSameSite": false,
75
+ "postLogoutRedirectUri": "http://localhost:3000/",
76
+ "sessionSecret": "{env:SECRET_FROM_ENV}",
77
+ "sessionStoreType": "memory",
78
+ "proxyTargets": {
79
+ "/api": "http://localhost:8080/api"
80
+ }
81
+ }
82
+ ```
83
+
84
+ By default it looks for a file named `bff.config.json`. But this can be overridden:
85
+
86
+ Vite:
87
+
88
+ ```ts
89
+ plugins: [
90
+ bff({configFile: ['bff.config.local.json', 'bff.config.json']}) //First existing file is used
91
+ ]
92
+ ```
93
+
94
+ Standalone:
95
+
96
+ ```shell
97
+ auth-bff --configFile /path/to/bff.config.json
98
+ ```
99
+
100
+ The config file supports two special forms for loading values from other sources. Primarily meant for loading secrets:
101
+
102
+ Environment values:
103
+
104
+ ```json
105
+ {
106
+ "sessionSecret": "{env:MY_SECRET}"
107
+ }
108
+ ```
109
+
110
+ AWS Parameter store:
111
+
112
+ ```json
113
+ {
114
+ "sessionSecret": "{ssm:/name/of/parameter}"
115
+ }
116
+ ```
117
+
118
+ This loads from the configured AWS environment. For this to work on your local machine the `AWS_PROFILE` environment
119
+ variable must be set, and you must be signed in to that profile
120
+
121
+ See [config.ts](src/config.ts) description of all config parameters
122
+
123
+ ### Using with ID-porten (via `okdata`):
124
+
125
+ `auth-bff` Has special support for keys generated by [`okdata`](https://github.com/oslokommune/okdata-cli).
126
+
127
+ 1. Start by creating a new client and key using okdata:
128
+
129
+ ```shell
130
+ ~> okdata pubs create-client
131
+ * Environment test
132
+ * Team <team>
133
+ * Client type ID-porten
134
+ * Integration name (identifying in which system or case this client will be used) <your name here>
135
+ * Redirect URIs (comma-separated) http://localhost:3000/auth/callback
136
+ * Post logout Redirect URIs (comma-separated) http://localhost:3000
137
+ * Frontchannel logout URI http://localhost:3000/auth/logout
138
+ * Client URI (back URI) http://localhost:3000
139
+ ...
140
+ ~> okdata pubs create-key
141
+ * Environment test
142
+ * Client <same client as above>
143
+ * Where should the key be stored? Send the key to your AWS Parameter Store
144
+ * AWS account number <accno>
145
+ * AWS region eu-west-1 (Ireland)
146
+ * Automatic key rotation will replace the key in SSM nightly on weekdays.
147
+ Enable automatic key rotation for this client? Yes
148
+ ...
149
+ A new key has been created and the following parameters have been written to SSM:
150
+ - /okdata/maskinporten/11111111-2222-3333-4444-555555555555/key.json
151
+ ...
152
+ ```
153
+
154
+ 2. Set the following options in the config file:
155
+
156
+ ```json
157
+ {
158
+ "issuer": "https://test.idporten.no/",
159
+ "clientId": "11111111-2222-3333-4444-555555555555",
160
+ "okDataIdPortenKeyName": "/okdata/maskinporten/11111111-2222-3333-4444-555555555555/key.json",
161
+ ...
162
+ }
163
+ ```
164
+
165
+ 3. Done!
166
+
167
+ ## Configuring session storage
168
+
169
+ Currently only dynamoDb is supported for storing sessions in production. It requires some setup.
170
+ To work properly, the table must have ttl enabled, and an extra index for the `idp-sid`-property (used to delete
171
+ sessions during front-channel logout)
172
+
173
+ > [!WARNING]
174
+ > If the table does not exist, it will be automatically created with settings not appropriate for production.
175
+
176
+
177
+ Here is an example configuration in terraform
178
+
179
+ ```terraform
180
+ resource "aws_dynamodb_table" "session_dynamodb_table" {
181
+ name = "${local.environment}-${local.main_container_name}-sessions"
182
+ billing_mode = "PAY_PER_REQUEST"
183
+ hash_key = "id"
184
+
185
+ on_demand_throughput {
186
+ max_read_request_units = 20
187
+ max_write_request_units = 20
188
+ }
189
+
190
+ attribute {
191
+ name = "id"
192
+ type = "S"
193
+ }
194
+
195
+ attribute {
196
+ name = "idp-sid"
197
+ type = "S"
198
+ }
199
+
200
+ ttl {
201
+ enabled = true
202
+ attribute_name = "expires"
203
+ }
204
+
205
+ global_secondary_index {
206
+ hash_key = "idp-sid"
207
+ name = "idp-sid-index"
208
+ projection_type = "KEYS_ONLY"
209
+ on_demand_throughput {
210
+ max_read_request_units = 20
211
+ max_write_request_units = 20
212
+ }
213
+ }
214
+ }
215
+ ```
216
+
217
+ The read/write limits above are just an example, and can be changed/removed
218
+
219
+ ## Required AWS permissions
220
+
221
+ When using SSM parameters or `okDataIdPortenKeyName` in the config, the service will need the `ssm:GetParameter`
222
+ permission for each parameter
223
+
224
+ If using dynamodb, the service will need the following permissions for the table
225
+
226
+ ```
227
+ dynamodb:CreateTable
228
+ dynamodb:DescribeTable
229
+ dynamodb:PutItem
230
+ dynamodb:DeleteItem
231
+ dynamodb:GetItem
232
+ dynamodb:Scan
233
+ dynamodb:UpdateItem
234
+ ```
235
+
236
+ ## React AuthContext
237
+
238
+ This package also includes a React component for handling authentication state
239
+
240
+
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-beta2",
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 access 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,10 @@
1
+ import { ViteDevServer } from 'vite';
2
+ export default function bff({ configFile }?: {
3
+ configFile?: string;
4
+ }): {
5
+ name: string;
6
+ apply: string;
7
+ configureServer: ({ middlewares }: ViteDevServer) => Promise<void>;
8
+ configurePreviewServer: ({ middlewares }: ViteDevServer) => Promise<void>;
9
+ };
10
+ //# 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,EAAC,aAAa,EAAC,MAAM,MAAM,CAAA;AAsBlC,MAAM,CAAC,OAAO,UAAU,GAAG,CAAC,EAAC,UAAU,EAAC,GAAE;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAM;;;uCAnBrC,aAAa;8CAAb,aAAa;EA0B3C"}
@@ -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-beta2",
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
  }