@oslokommune/auth-bff 1.6.0 → 2.0.0-beta1

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 (100) hide show
  1. package/dist/package.json +57 -0
  2. package/dist/src/OpenIdConfigManager.d.ts +10 -0
  3. package/dist/src/OpenIdConfigManager.d.ts.map +1 -0
  4. package/dist/src/OpenIdConfigManager.js +77 -0
  5. package/dist/src/config.d.ts +25 -0
  6. package/dist/src/config.d.ts.map +1 -0
  7. package/dist/src/config.js +55 -0
  8. package/dist/src/middleware/OidcMiddleware.d.ts +20 -0
  9. package/dist/src/middleware/OidcMiddleware.d.ts.map +1 -0
  10. package/dist/src/middleware/OidcMiddleware.js +231 -0
  11. package/dist/src/middleware/oidc-routes.d.mts.map +1 -0
  12. package/dist/src/middleware/proxy-routes.d.mts.map +1 -0
  13. package/dist/{middleware → src/middleware}/proxy-routes.mjs +5 -5
  14. package/dist/src/middleware/security-headers.d.mts.map +1 -0
  15. package/dist/{middleware → src/middleware}/security-headers.mjs +2 -2
  16. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.mts +3 -0
  17. package/dist/src/middleware/sessions/dynamoDbSessionStore.d.mts.map +1 -0
  18. package/dist/{middleware → src/middleware}/sessions/dynamoDbSessionStore.mjs +12 -17
  19. package/dist/src/middleware/sessions/memorySessionStore.d.mts +3 -0
  20. package/dist/src/middleware/sessions/memorySessionStore.d.mts.map +1 -0
  21. package/dist/src/middleware/sessions/sessions.d.mts +2 -0
  22. package/dist/src/middleware/sessions/sessions.d.mts.map +1 -0
  23. package/dist/{middleware → src/middleware}/sessions/sessions.mjs +3 -4
  24. package/dist/src/middleware/static-routes.d.mts.map +1 -0
  25. package/dist/src/react/AuthContext.d.ts.map +1 -0
  26. package/dist/src/react/AuthContextProvider.d.ts.map +1 -0
  27. package/dist/{react → src/react}/AuthContextProvider.jsx +15 -27
  28. package/dist/src/react/UseAuthContext.d.ts +2 -0
  29. package/dist/src/react/UseAuthContext.d.ts.map +1 -0
  30. package/dist/{react → src/react}/UseAuthContext.jsx +2 -2
  31. package/dist/src/react/global-user.d.ts.map +1 -0
  32. package/dist/src/react/index.d.ts +5 -0
  33. package/dist/src/react/index.d.ts.map +1 -0
  34. package/dist/src/react/index.js +4 -0
  35. package/dist/src/react/poller.d.ts.map +1 -0
  36. package/dist/src/react/poller.js +28 -0
  37. package/dist/{server.d.mts.map → src/server.d.mts.map} +1 -1
  38. package/dist/{server.mjs → src/server.mjs} +3 -3
  39. package/dist/{utils.d.ts.map → src/utils.d.ts.map} +1 -1
  40. package/dist/src/utils.js +3 -0
  41. package/dist/src/vite-plugin.d.mts.map +1 -0
  42. package/dist/src/vite-plugin.mjs +35 -0
  43. package/package.json +16 -9
  44. package/dist/client.d.mts +0 -7
  45. package/dist/client.d.mts.map +0 -1
  46. package/dist/client.mjs +0 -92
  47. package/dist/config-utils.d.mts +0 -2
  48. package/dist/config-utils.d.mts.map +0 -1
  49. package/dist/config-utils.mjs +0 -1
  50. package/dist/config.d.mts +0 -4
  51. package/dist/config.d.mts.map +0 -1
  52. package/dist/config.mjs +0 -69
  53. package/dist/middleware/oidc-routes.d.mts.map +0 -1
  54. package/dist/middleware/oidc.d.mts +0 -17
  55. package/dist/middleware/oidc.d.mts.map +0 -1
  56. package/dist/middleware/oidc.mjs +0 -220
  57. package/dist/middleware/proxy-routes.d.mts.map +0 -1
  58. package/dist/middleware/security-headers.d.mts.map +0 -1
  59. package/dist/middleware/sessions/dynamoDbSessionStore.d.mts +0 -2
  60. package/dist/middleware/sessions/dynamoDbSessionStore.d.mts.map +0 -1
  61. package/dist/middleware/sessions/memorySessionStore.d.mts +0 -2
  62. package/dist/middleware/sessions/memorySessionStore.d.mts.map +0 -1
  63. package/dist/middleware/sessions/sessions.d.mts +0 -2
  64. package/dist/middleware/sessions/sessions.d.mts.map +0 -1
  65. package/dist/middleware/sessions.d.mts +0 -2
  66. package/dist/middleware/sessions.d.mts.map +0 -1
  67. package/dist/middleware/sessions.mjs +0 -87
  68. package/dist/middleware/static-routes.d.mts.map +0 -1
  69. package/dist/react/AuthContext.d.ts.map +0 -1
  70. package/dist/react/AuthContextProvider.d.ts.map +0 -1
  71. package/dist/react/UseAuthContext.d.ts +0 -2
  72. package/dist/react/UseAuthContext.d.ts.map +0 -1
  73. package/dist/react/checker.d.ts +0 -3
  74. package/dist/react/checker.d.ts.map +0 -1
  75. package/dist/react/checker.js +0 -43
  76. package/dist/react/global-user.d.ts.map +0 -1
  77. package/dist/react/index.d.ts +0 -5
  78. package/dist/react/index.d.ts.map +0 -1
  79. package/dist/react/index.js +0 -4
  80. package/dist/react/poller.d.ts.map +0 -1
  81. package/dist/react/poller.js +0 -39
  82. package/dist/utils.js +0 -3
  83. package/dist/vite-plugin.d.mts.map +0 -1
  84. package/dist/vite-plugin.mjs +0 -44
  85. /package/dist/{middleware → src/middleware}/oidc-routes.d.mts +0 -0
  86. /package/dist/{middleware → src/middleware}/oidc-routes.mjs +0 -0
  87. /package/dist/{middleware → src/middleware}/proxy-routes.d.mts +0 -0
  88. /package/dist/{middleware → src/middleware}/security-headers.d.mts +0 -0
  89. /package/dist/{middleware → src/middleware}/sessions/memorySessionStore.mjs +0 -0
  90. /package/dist/{middleware → src/middleware}/static-routes.d.mts +0 -0
  91. /package/dist/{middleware → src/middleware}/static-routes.mjs +0 -0
  92. /package/dist/{react → src/react}/AuthContext.d.ts +0 -0
  93. /package/dist/{react → src/react}/AuthContext.jsx +0 -0
  94. /package/dist/{react → src/react}/AuthContextProvider.d.ts +0 -0
  95. /package/dist/{react → src/react}/global-user.d.ts +0 -0
  96. /package/dist/{react → src/react}/global-user.js +0 -0
  97. /package/dist/{react → src/react}/poller.d.ts +0 -0
  98. /package/dist/{server.d.mts → src/server.d.mts} +0 -0
  99. /package/dist/{utils.d.ts → src/utils.d.ts} +0 -0
  100. /package/dist/{vite-plugin.d.mts → src/vite-plugin.d.mts} +0 -0
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@oslokommune/auth-bff",
3
+ "version": "2.0.0-beta1",
4
+ "repository": "https://github.com/oslokommune/auth-bff.git",
5
+ "publishConfig": {
6
+ "access": "public"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "run": "node ./dist/server.mjs",
11
+ "build-and-publish": "tsc && npm publish",
12
+ "build-and-publish-prerelease": "tsc && npm publish --tag prerelease",
13
+ "test": "vitest"
14
+ },
15
+ "exports": {
16
+ "./vite-plugin": "./dist/src/vite-plugin.mjs",
17
+ "./react": "./dist/src/react/index.js"
18
+ },
19
+ "bin": {
20
+ "auth-bff": "dist/src/server.mjs"
21
+ },
22
+ "files": [
23
+ "/dist"
24
+ ],
25
+ "type": "module",
26
+ "author": "",
27
+ "license": "",
28
+ "description": "",
29
+ "devDependencies": {
30
+ "@types/compression": "^1.8.1",
31
+ "@types/express": "^4.17.22",
32
+ "@types/express-session": "^1.18.2",
33
+ "@types/node-forge": "1.3.13",
34
+ "@types/react": "17.0.87",
35
+ "@types/supertest": "^6.0.3",
36
+ "react": "17.0.2",
37
+ "supertest": "^7.2.2",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.18"
40
+ },
41
+ "dependencies": {
42
+ "@aws-sdk/client-dynamodb": "^3.817.0",
43
+ "@aws-sdk/client-ssm": "^3.817.0",
44
+ "command-line-args": "^6.0.1",
45
+ "compression": "^1.8.0",
46
+ "connect-dynamodb": "^3.0.5",
47
+ "express": "4.21.2",
48
+ "express-session": "1.18.2",
49
+ "find-up": "^7.0.0",
50
+ "helmet": "^8.1.0",
51
+ "http-proxy-middleware": "^3.0.5",
52
+ "jose": "^6.0.11",
53
+ "node-forge": "1.3.3",
54
+ "openid-client": "^6.8.1",
55
+ "string-replace-middleware": "^1.1.0"
56
+ }
57
+ }
@@ -0,0 +1,10 @@
1
+ import * as client from 'openid-client';
2
+ import { BffConfig } from "./config.js";
3
+ export declare class OpenIdConfigManager {
4
+ #private;
5
+ constructor(config: BffConfig, serverMetadata?: client.ServerMetadata);
6
+ init(): Promise<void>;
7
+ updateOpenIdConfig(): Promise<void>;
8
+ get openIdConfig(): client.Configuration;
9
+ }
10
+ //# sourceMappingURL=OpenIdConfigManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OpenIdConfigManager.d.ts","sourceRoot":"","sources":["../../src/OpenIdConfigManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,EAAC,SAAS,EAAkB,MAAM,aAAa,CAAC;AASvD,qBAAa,mBAAmB;;gBAMlB,MAAM,EAAE,SAAS,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,cAAc;IAK/D,IAAI;IA4BJ,kBAAkB;IAuCxB,IAAI,YAAY,yBAEf;CAEF"}
@@ -0,0 +1,77 @@
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 _OpenIdConfigManager_instances, _OpenIdConfigManager_bffConfig, _OpenIdConfigManager_openIdConfig, _OpenIdConfigManager_serverMetadata, _OpenIdConfigManager_p12ToJwk, _OpenIdConfigManager_createKeyFromOkData;
13
+ // @ts-ignore
14
+ import forge from 'node-forge';
15
+ import * as jose from 'jose';
16
+ import * as client from 'openid-client';
17
+ import { getSsmParameter } from "./config.js";
18
+ export class OpenIdConfigManager {
19
+ constructor(config, serverMetadata) {
20
+ _OpenIdConfigManager_instances.add(this);
21
+ _OpenIdConfigManager_bffConfig.set(this, void 0);
22
+ _OpenIdConfigManager_openIdConfig.set(this, void 0);
23
+ _OpenIdConfigManager_serverMetadata.set(this, void 0);
24
+ __classPrivateFieldSet(this, _OpenIdConfigManager_bffConfig, config, "f");
25
+ __classPrivateFieldSet(this, _OpenIdConfigManager_serverMetadata, serverMetadata, "f");
26
+ }
27
+ async init() {
28
+ await this.updateOpenIdConfig();
29
+ if (__classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").okDataIdPortenKeyName) {
30
+ setInterval(async () => {
31
+ await this.updateOpenIdConfig();
32
+ }, 5 * 60 * 1000);
33
+ }
34
+ }
35
+ async updateOpenIdConfig() {
36
+ console.log('Updating OpenId config...');
37
+ let key;
38
+ if (__classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").okDataIdPortenKeyName) {
39
+ console.log('Fetching okdata key');
40
+ key = await __classPrivateFieldGet(this, _OpenIdConfigManager_instances, "m", _OpenIdConfigManager_createKeyFromOkData).call(this, __classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").okDataIdPortenKeyName);
41
+ }
42
+ const clientId = __classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").clientId;
43
+ const clientMetadata = { client_secret: __classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").clientSecret };
44
+ const clientAuth = key ? client.PrivateKeyJwt(key) : undefined;
45
+ if (__classPrivateFieldGet(this, _OpenIdConfigManager_openIdConfig, "f")) {
46
+ console.log('Reusing OpenId Config with new key');
47
+ __classPrivateFieldSet(this, _OpenIdConfigManager_openIdConfig, new client.Configuration(__classPrivateFieldGet(this, _OpenIdConfigManager_openIdConfig, "f").serverMetadata(), clientId, clientMetadata, clientAuth), "f");
48
+ }
49
+ else if (__classPrivateFieldGet(this, _OpenIdConfigManager_serverMetadata, "f")) {
50
+ console.log('Using OpenId Config with provided server metadata');
51
+ __classPrivateFieldSet(this, _OpenIdConfigManager_openIdConfig, new client.Configuration(__classPrivateFieldGet(this, _OpenIdConfigManager_serverMetadata, "f"), clientId, clientMetadata, clientAuth), "f");
52
+ }
53
+ else {
54
+ console.log('Fetching OpenId config');
55
+ __classPrivateFieldSet(this, _OpenIdConfigManager_openIdConfig, await client.discovery(new URL(__classPrivateFieldGet(this, _OpenIdConfigManager_bffConfig, "f").issuer), clientId, clientMetadata, clientAuth), "f");
56
+ }
57
+ }
58
+ get openIdConfig() {
59
+ return __classPrivateFieldGet(this, _OpenIdConfigManager_openIdConfig, "f");
60
+ }
61
+ }
62
+ _OpenIdConfigManager_bffConfig = new WeakMap(), _OpenIdConfigManager_openIdConfig = new WeakMap(), _OpenIdConfigManager_serverMetadata = new WeakMap(), _OpenIdConfigManager_instances = new WeakSet(), _OpenIdConfigManager_p12ToJwk = async function _OpenIdConfigManager_p12ToJwk(okdataP12) {
63
+ //TODO: dette er helt sikkert mulig å gjøre i færre steg...
64
+ const p12Der = forge.util.decode64(okdataP12.keystore);
65
+ const p12Asn1 = forge.asn1.fromDer(p12Der);
66
+ const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, okdataP12.key_password);
67
+ const privateKey = p12.getBags({ friendlyName: okdataP12.key_alias }).friendlyName[0].key;
68
+ const privateKeyAsn1 = forge.pki.privateKeyToAsn1(privateKey);
69
+ const privateKeyInfo = forge.pki.wrapRsaPrivateKey(privateKeyAsn1);
70
+ const pem = forge.pki.privateKeyInfoToPem(privateKeyInfo);
71
+ const key = await jose.importPKCS8(pem, 'RS256', { extractable: true });
72
+ return { key: key, kid: okdataP12.key_id };
73
+ }, _OpenIdConfigManager_createKeyFromOkData = async function _OpenIdConfigManager_createKeyFromOkData(ssmName) {
74
+ const keyString = await getSsmParameter(ssmName);
75
+ const okdataKey = JSON.parse(keyString);
76
+ return await __classPrivateFieldGet(this, _OpenIdConfigManager_instances, "m", _OpenIdConfigManager_p12ToJwk).call(this, okdataKey);
77
+ };
@@ -0,0 +1,25 @@
1
+ export type BffConfig = {
2
+ basePath?: string;
3
+ staticRootPath?: string;
4
+ issuer: string;
5
+ clientId: string;
6
+ clientSecret?: string;
7
+ redirectUri: string;
8
+ resources: Array<string>;
9
+ cookiePath?: string;
10
+ cookieSecure?: Boolean;
11
+ cookieSameSite: Boolean | string;
12
+ postLogoutRedirectUri: string;
13
+ okDataIdPortenKeyName: string;
14
+ sessionSecret: string;
15
+ sessionStoreType: 'memory' | 'dynamodb';
16
+ sessionStoreOptions?: object;
17
+ proxyTargets: {
18
+ [path: string]: string;
19
+ };
20
+ userClaims: Array<string>;
21
+ };
22
+ export declare function getEnv(env: string, defaultVal?: string, parseFn?: (val: string) => string): string;
23
+ export declare function getSsmParameter(name: string, withDecryption?: boolean): Promise<string>;
24
+ export declare function loadConfig(configFile?: string): Promise<BffConfig>;
25
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,55 @@
1
+ import { findUp } from 'find-up';
2
+ import { GetParameterCommand, SSMClient } from "@aws-sdk/client-ssm";
3
+ export function getEnv(env, defaultVal, parseFn) {
4
+ if (process.env[env]) {
5
+ return parseFn ? parseFn(process.env[env]) : process.env[env];
6
+ }
7
+ else if (defaultVal !== undefined) {
8
+ return defaultVal;
9
+ }
10
+ else {
11
+ throw Error(`Missing env var: ${env}`);
12
+ }
13
+ }
14
+ let ssmClient;
15
+ export async function getSsmParameter(name, withDecryption = true) {
16
+ ssmClient ?? (ssmClient = new SSMClient({}));
17
+ return ssmClient.send(new GetParameterCommand({
18
+ Name: name,
19
+ WithDecryption: withDecryption
20
+ })).then(p => p.Parameter.Value);
21
+ }
22
+ const defaultConfig = {
23
+ basePath: "",
24
+ cookiePath: '/',
25
+ cookieSecure: true,
26
+ cookieSameSite: 'lax',
27
+ staticRootPath: './dist'
28
+ };
29
+ let config;
30
+ export async function loadConfig(configFile = 'bff.config.json') {
31
+ if (config)
32
+ return config;
33
+ const userConfigPath = await findUp(configFile);
34
+ if (!userConfigPath) {
35
+ throw Error(`Could not find config file ${configFile}`);
36
+ }
37
+ console.log('Loading config at', userConfigPath);
38
+ const { default: loadedConfig } = await import(userConfigPath, { with: { type: 'json' } });
39
+ for (const [key, value] of Object.entries(loadedConfig)) {
40
+ if (typeof value === "string") {
41
+ const [, varType, varName] = value.match(/\{(\w+):(.*)}/) ?? [];
42
+ if (varType === 'env') {
43
+ loadedConfig[key] = getEnv(varName);
44
+ }
45
+ else if (varType === 'ssm') {
46
+ loadedConfig[key] = await getSsmParameter(varName);
47
+ }
48
+ else if (varType) {
49
+ throw Error(`unknown varType: ${varType}`);
50
+ }
51
+ }
52
+ }
53
+ config = { ...defaultConfig, ...loadedConfig };
54
+ return config;
55
+ }
@@ -0,0 +1,20 @@
1
+ import { OpenIdConfigManager } from "../OpenIdConfigManager.js";
2
+ import { BffConfig } from "../config.js";
3
+ import type { Request, Response, NextFunction } from 'express';
4
+ export declare class OidcMiddleware {
5
+ #private;
6
+ /**
7
+ * @private
8
+ * @param config
9
+ * @param configManager
10
+ */
11
+ constructor(config: BffConfig, configManager: OpenIdConfigManager);
12
+ static create(config: BffConfig): Promise<OidcMiddleware>;
13
+ get ensureFreshToken(): (req: Request, res: Response, next: NextFunction) => void;
14
+ get login(): (req: Request, res: Response, next: NextFunction) => Promise<void>;
15
+ get callback(): (req: Request, res: Response, next: NextFunction) => Promise<void>;
16
+ get user(): (req: Request, res: Response, next: NextFunction) => Promise<Response<any, Record<string, any>>>;
17
+ get logout(): (req: Request, res: Response) => void;
18
+ get frontChannelLogout(): (req: Request, res: Response) => Promise<void>;
19
+ }
20
+ //# sourceMappingURL=OidcMiddleware.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,231 @@
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
+ return res.sendStatus(401);
125
+ }
126
+ return res.send(req.session.userClaims);
127
+ }
128
+ catch (e) {
129
+ console.error(`Error in /user sid=${redact(req.session?.id)}`, e);
130
+ next(e);
131
+ }
132
+ };
133
+ }
134
+ get logout() {
135
+ return (req, res) => {
136
+ const tokenResponse = req.session.tokenResponse;
137
+ req.session.destroy(() => {
138
+ const endSessionUrl = openIdClient.buildEndSessionUrl(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), {
139
+ id_token_hint: tokenResponse?.id_token,
140
+ });
141
+ res.redirect(endSessionUrl.toString());
142
+ });
143
+ };
144
+ }
145
+ get frontChannelLogout() {
146
+ return async (req, res) => {
147
+ const { iss, sid } = req.query;
148
+ console.log(`Front channel logout: params iss=${iss}, sid=${redact(sid)}`);
149
+ if (sid) {
150
+ try {
151
+ await req.destroySessionByIdpSid?.(sid);
152
+ }
153
+ catch (e) {
154
+ console.error("Failed to destroy session", e);
155
+ }
156
+ }
157
+ res.sendStatus(200);
158
+ };
159
+ }
160
+ }
161
+ _OidcMiddleware_configManager = new WeakMap(), _OidcMiddleware_bffConfig = new WeakMap(), _OidcMiddleware_refreshPromises = new WeakMap(), _OidcMiddleware_instances = new WeakSet(), _OidcMiddleware_openIdConfig_get = function _OidcMiddleware_openIdConfig_get() {
162
+ return __classPrivateFieldGet(this, _OidcMiddleware_configManager, "f").openIdConfig;
163
+ }, _OidcMiddleware_refreshTokens = async function _OidcMiddleware_refreshTokens(req, tokenResponse) {
164
+ var _a;
165
+ const sessionId = req.session.id;
166
+ const refreshToken = tokenResponse.refresh_token;
167
+ const doRefresh = async () => {
168
+ console.log(`Token refresh starting. sid=${redact(sessionId)}`);
169
+ try {
170
+ const tokenResponse = await openIdClient.refreshTokenGrant(__classPrivateFieldGet(this, _OidcMiddleware_instances, "a", _OidcMiddleware_openIdConfig_get), refreshToken);
171
+ console.log(`Token refresh OK. sid=${redact(sessionId)}`);
172
+ return tokenResponse;
173
+ }
174
+ catch (err) {
175
+ console.log(`Token refresh failed. sid=${redact(sessionId)}`, err);
176
+ return null;
177
+ }
178
+ };
179
+ const refreshPromise = (_a = __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f"))[refreshToken] ?? (_a[refreshToken] = doRefresh().finally(() => {
180
+ console.log(`Token refresh finished. Cleaning up. sid=${redact(sessionId)}`);
181
+ setTimeout(() => {
182
+ delete __classPrivateFieldGet(this, _OidcMiddleware_refreshPromises, "f")[refreshToken];
183
+ }, 10000);
184
+ }));
185
+ const refreshedTokenResponse = await refreshPromise;
186
+ if (refreshedTokenResponse) {
187
+ Object.assign(req.session.tokenResponse, refreshedTokenResponse);
188
+ req.session.accessTokenExpiresAt = __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_getAccessTokenExpiryTime).call(this, refreshedTokenResponse);
189
+ }
190
+ else {
191
+ req.session.tokenResponse = null;
192
+ }
193
+ return req.session.tokenResponse;
194
+ }, _OidcMiddleware_getFreshTokens = async function _OidcMiddleware_getFreshTokens(req) {
195
+ const tokenResponse = req.session.tokenResponse;
196
+ if (!tokenResponse) {
197
+ console.log(`No tokenResponse found in session sid=${redact(req.session.id)}`);
198
+ return;
199
+ }
200
+ const now = new Date().getTime();
201
+ const expiresAt = req.session.accessTokenExpiresAt || 0;
202
+ /*if(!expiresAt) {
203
+ //For at ting ikke skal eksplodere hvis man får inn en gammel session.
204
+ //TODO: denne kan fjernes når den har kjørt i prod i et døgn+
205
+ console.error('accessTokenExpiresAt was not set')
206
+ return
207
+ }*/
208
+ const expiresInSeconds = (expiresAt - now) / 1000;
209
+ if (expiresInSeconds < 5) {
210
+ console.log(`Access token expired. sid=${redact(req.session.id)}, expiresInSeconds=${expiresInSeconds}`);
211
+ return await __classPrivateFieldGet(this, _OidcMiddleware_instances, "m", _OidcMiddleware_refreshTokens).call(this, req, tokenResponse);
212
+ }
213
+ else {
214
+ return tokenResponse;
215
+ }
216
+ }, _OidcMiddleware_getUserClaims = function _OidcMiddleware_getUserClaims(tokenResponse) {
217
+ let claims = tokenResponse.claims();
218
+ if (__classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").userClaims) {
219
+ claims = __classPrivateFieldGet(this, _OidcMiddleware_bffConfig, "f").userClaims.reduce((acc, claim) => {
220
+ acc[claim] = claims[claim];
221
+ return acc;
222
+ }, {});
223
+ }
224
+ return claims;
225
+ }, _OidcMiddleware_getAccessTokenExpiryTime = function _OidcMiddleware_getAccessTokenExpiryTime(tokenResponse) {
226
+ if (tokenResponse.expires_in !== undefined) {
227
+ const now = new Date();
228
+ now.setSeconds(now.getSeconds() + tokenResponse.expires_in);
229
+ return now.getTime();
230
+ }
231
+ };
@@ -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 @@
1
+ {"version":3,"file":"proxy-routes.d.mts","sourceRoot":"","sources":["../../../src/middleware/proxy-routes.mjs"],"names":[],"mappings":"AAGA,mEA4BC"}
@@ -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.originalUrl} -> ${target}${req.originalUrl}: ${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"}
@@ -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"}
@@ -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,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"}