@forestadmin/agent-bff 1.0.0 → 1.2.0

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.
package/README.md CHANGED
@@ -43,6 +43,25 @@ yarn start:dev # node --env-file=.env dist/cli.js
43
43
  - A required var that is absent (or empty / whitespace-only) does not crash the server. It boots and
44
44
  reports the gap through `/health` (503 `degraded`).
45
45
 
46
+ ## Sessions & token rotation
47
+
48
+ OAuth sessions, opaque refresh tokens, and rotated-out-token reuse detection are held in an
49
+ **in-memory, single-process** store. Reuse detection has a one-step grace window: re-presenting the
50
+ **most recently** rotated-out token (a benign retry of a refresh whose response was lost) replays
51
+ the last issued pair instead of killing the session — the stored refresh token is returned with a
52
+ freshly minted access JWT. Re-presenting any **older** rotated-out token still invalidates the whole
53
+ session, so a stolen earlier token stays detectable. The grace window is bounded by the rotation
54
+ chain, not a timer: once the client advances one more rotation, the previous token is no longer the
55
+ latest and falls back to reuse. These guarantees are only correct within one process. Two layers
56
+ are process-local: the session/rotation maps in the store, and the in-flight refresh dedup maps
57
+ (`inFlightRefreshByPresentedHash` in `oauth-routes`, `inFlightRefreshesBySid` in
58
+ `session-lifecycle`) that collapse concurrent refreshes of the same token into a single rotation.
59
+ Running the BFF behind a load balancer without sticky sessions breaks reuse detection and the
60
+ single-rotation guarantee — and a shared session store alone does not fix it: two concurrent
61
+ refreshes landing on different nodes would each try to rotate, one committing and the other failing
62
+ with `session_expired`. Horizontal scaling requires both a shared session store **and** a shared
63
+ (or node-pinned) refresh-coordination mechanism.
64
+
46
65
  ## `/health`
47
66
 
48
67
  ```jsonc
package/dist/cli-core.js CHANGED
@@ -5,14 +5,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.default = runCli;
7
7
  exports.reportFatalError = reportFatalError;
8
+ const bodyparser_1 = require("@koa/bodyparser");
8
9
  const console_logger_1 = __importDefault(require("./adapters/console-logger"));
9
10
  const env_config_1 = require("./config/env-config");
10
11
  const errors_1 = require("./errors");
11
12
  const bff_http_server_1 = __importDefault(require("./http/bff-http-server"));
13
+ const forest_server_client_1 = __importDefault(require("./oauth/forest-server-client"));
14
+ const oauth_routes_1 = __importDefault(require("./oauth/oauth-routes"));
15
+ const session_store_1 = __importDefault(require("./oauth/session-store"));
16
+ const token_cipher_1 = __importDefault(require("./oauth/token-cipher"));
12
17
  const version_1 = __importDefault(require("./version"));
18
+ const BODY_LIMIT = '16kb';
19
+ const SESSION_TTL_SECONDS = 24 * 60 * 60;
20
+ function resolveOAuthConfig(config) {
21
+ const { forestServerUrl, forestEnvSecret, forestAppUrl, forestAuthSecret, tokenEncryptionKey } = config;
22
+ if (forestServerUrl &&
23
+ forestEnvSecret &&
24
+ forestAppUrl &&
25
+ forestAuthSecret &&
26
+ tokenEncryptionKey) {
27
+ return { forestServerUrl, forestEnvSecret, forestAppUrl, forestAuthSecret, tokenEncryptionKey };
28
+ }
29
+ return undefined;
30
+ }
31
+ async function buildOAuthMiddlewares(config, logger) {
32
+ const oauthConfig = resolveOAuthConfig(config);
33
+ if (!oauthConfig) {
34
+ logger('Warn', 'OAuth routes disabled: required configuration is missing');
35
+ return [];
36
+ }
37
+ const { forestServerUrl, forestEnvSecret, forestAppUrl, forestAuthSecret, tokenEncryptionKey } = oauthConfig;
38
+ const serverClient = new forest_server_client_1.default({ forestServerUrl, envSecret: forestEnvSecret });
39
+ const environmentId = await serverClient.fetchEnvironmentId();
40
+ const sessionStore = (0, session_store_1.default)({
41
+ cipher: (0, token_cipher_1.default)(tokenEncryptionKey),
42
+ now: () => Date.now(),
43
+ sessionTtlSeconds: SESSION_TTL_SECONDS,
44
+ });
45
+ const oauthRoutes = (0, oauth_routes_1.default)({
46
+ serverClient,
47
+ sessionStore,
48
+ forestAppUrl,
49
+ authSecret: forestAuthSecret,
50
+ environmentId,
51
+ logger,
52
+ });
53
+ return [(0, bodyparser_1.bodyParser)({ jsonLimit: BODY_LIMIT }), oauthRoutes];
54
+ }
13
55
  async function runCli(env, logger = (0, console_logger_1.default)()) {
14
56
  const config = (0, env_config_1.parseConfig)(env);
15
- const server = new bff_http_server_1.default({ port: config.httpPort, version: version_1.default, config, logger });
57
+ const middlewares = await buildOAuthMiddlewares(config, logger);
58
+ const server = new bff_http_server_1.default({
59
+ port: config.httpPort,
60
+ version: version_1.default,
61
+ config,
62
+ logger,
63
+ middlewares,
64
+ });
16
65
  await server.start();
17
66
  return server;
18
67
  }
@@ -20,4 +69,4 @@ function reportFatalError(err) {
20
69
  process.stderr.write(`Error: ${(0, errors_1.extractErrorMessage)(err)}\n`);
21
70
  process.exitCode = 1;
22
71
  }
23
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWNvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2xpLWNvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUFRQSx5QkFVQztBQUVELDRDQUdDO0FBckJELCtFQUE0RDtBQUM1RCxvREFBa0Q7QUFDbEQscUNBQStDO0FBQy9DLDZFQUFtRDtBQUNuRCx3REFBZ0M7QUFFakIsS0FBSyxVQUFVLE1BQU0sQ0FDbEMsR0FBc0IsRUFDdEIsU0FBaUIsSUFBQSx3QkFBbUIsR0FBRTtJQUV0QyxNQUFNLE1BQU0sR0FBRyxJQUFBLHdCQUFXLEVBQUMsR0FBRyxDQUFDLENBQUM7SUFDaEMsTUFBTSxNQUFNLEdBQUcsSUFBSSx5QkFBYSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxRQUFRLEVBQUUsT0FBTyxFQUFQLGlCQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7SUFFckYsTUFBTSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFckIsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELFNBQWdCLGdCQUFnQixDQUFDLEdBQVk7SUFDM0MsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxJQUFBLDRCQUFtQixFQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3RCxPQUFPLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQztBQUN2QixDQUFDIn0=
72
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpLWNvcmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvY2xpLWNvcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7QUE2RUEseUJBaUJDO0FBRUQsNENBR0M7QUEvRkQsZ0RBQTZDO0FBRTdDLCtFQUE0RDtBQUM1RCxvREFBa0Q7QUFDbEQscUNBQStDO0FBQy9DLDZFQUFtRDtBQUNuRCx3RkFBOEQ7QUFDOUQsd0VBQXFEO0FBQ3JELDBFQUErRDtBQUMvRCx3RUFBcUQ7QUFDckQsd0RBQWdDO0FBRWhDLE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQztBQUMxQixNQUFNLG1CQUFtQixHQUFHLEVBQUUsR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFDO0FBVXpDLFNBQVMsa0JBQWtCLENBQUMsTUFBaUI7SUFDM0MsTUFBTSxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLEdBQzVGLE1BQU0sQ0FBQztJQUVULElBQ0UsZUFBZTtRQUNmLGVBQWU7UUFDZixZQUFZO1FBQ1osZ0JBQWdCO1FBQ2hCLGtCQUFrQixFQUNsQixDQUFDO1FBQ0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLENBQUM7SUFDbEcsQ0FBQztJQUVELE9BQU8sU0FBUyxDQUFDO0FBQ25CLENBQUM7QUFFRCxLQUFLLFVBQVUscUJBQXFCLENBQUMsTUFBaUIsRUFBRSxNQUFjO0lBQ3BFLE1BQU0sV0FBVyxHQUFHLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxDQUFDO0lBRS9DLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztRQUNqQixNQUFNLENBQUMsTUFBTSxFQUFFLDBEQUEwRCxDQUFDLENBQUM7UUFFM0UsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0lBRUQsTUFBTSxFQUFFLGVBQWUsRUFBRSxlQUFlLEVBQUUsWUFBWSxFQUFFLGdCQUFnQixFQUFFLGtCQUFrQixFQUFFLEdBQzVGLFdBQVcsQ0FBQztJQUVkLE1BQU0sWUFBWSxHQUFHLElBQUksOEJBQWtCLENBQUMsRUFBRSxlQUFlLEVBQUUsU0FBUyxFQUFFLGVBQWUsRUFBRSxDQUFDLENBQUM7SUFDN0YsTUFBTSxhQUFhLEdBQUcsTUFBTSxZQUFZLENBQUMsa0JBQWtCLEVBQUUsQ0FBQztJQUU5RCxNQUFNLFlBQVksR0FBRyxJQUFBLHVCQUEwQixFQUFDO1FBQzlDLE1BQU0sRUFBRSxJQUFBLHNCQUFpQixFQUFDLGtCQUFrQixDQUFDO1FBQzdDLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsR0FBRyxFQUFFO1FBQ3JCLGlCQUFpQixFQUFFLG1CQUFtQjtLQUN2QyxDQUFDLENBQUM7SUFFSCxNQUFNLFdBQVcsR0FBRyxJQUFBLHNCQUFpQixFQUFDO1FBQ3BDLFlBQVk7UUFDWixZQUFZO1FBQ1osWUFBWTtRQUNaLFVBQVUsRUFBRSxnQkFBZ0I7UUFDNUIsYUFBYTtRQUNiLE1BQU07S0FDUCxDQUFDLENBQUM7SUFFSCxPQUFPLENBQUMsSUFBQSx1QkFBVSxFQUFDLEVBQUUsU0FBUyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsV0FBVyxDQUFDLENBQUM7QUFDOUQsQ0FBQztBQUVjLEtBQUssVUFBVSxNQUFNLENBQ2xDLEdBQXNCLEVBQ3RCLFNBQWlCLElBQUEsd0JBQW1CLEdBQUU7SUFFdEMsTUFBTSxNQUFNLEdBQUcsSUFBQSx3QkFBVyxFQUFDLEdBQUcsQ0FBQyxDQUFDO0lBQ2hDLE1BQU0sV0FBVyxHQUFHLE1BQU0scUJBQXFCLENBQUMsTUFBTSxFQUFFLE1BQU0sQ0FBQyxDQUFDO0lBQ2hFLE1BQU0sTUFBTSxHQUFHLElBQUkseUJBQWEsQ0FBQztRQUMvQixJQUFJLEVBQUUsTUFBTSxDQUFDLFFBQVE7UUFDckIsT0FBTyxFQUFQLGlCQUFPO1FBQ1AsTUFBTTtRQUNOLE1BQU07UUFDTixXQUFXO0tBQ1osQ0FBQyxDQUFDO0lBRUgsTUFBTSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7SUFFckIsT0FBTyxNQUFNLENBQUM7QUFDaEIsQ0FBQztBQUVELFNBQWdCLGdCQUFnQixDQUFDLEdBQVk7SUFDM0MsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsVUFBVSxJQUFBLDRCQUFtQixFQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUM3RCxPQUFPLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQztBQUN2QixDQUFDIn0=
@@ -7,6 +7,7 @@ export interface BFFConfig {
7
7
  forestServerUrl?: string;
8
8
  forestAppUrl?: string;
9
9
  agentUrl?: string;
10
+ tokenEncryptionKey?: string;
10
11
  httpPort: number;
11
12
  presence: PresenceMap;
12
13
  hasAllRequired: boolean;
@@ -18,6 +18,8 @@ exports.REQUIRED_KEYS = [
18
18
  const URL_KEYS = ['FOREST_SERVER_URL', 'FOREST_APP_URL', 'AGENT_URL'];
19
19
  const DECIMAL_INTEGER = /^\d+$/;
20
20
  const MAX_PORT = 65535;
21
+ const ENCRYPTION_KEY_BYTES = 32;
22
+ const BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
21
23
  const HTTP_URL_SCHEMA = zod_1.z.url({ protocol: /^https?$/ });
22
24
  function normalize(value) {
23
25
  return value === undefined || value.trim() === '' ? undefined : value;
@@ -35,6 +37,16 @@ function parsePort(raw) {
35
37
  function isHttpUrl(value) {
36
38
  return !/\s/.test(value) && HTTP_URL_SCHEMA.safeParse(value).success;
37
39
  }
40
+ function isValidEncryptionKey(value) {
41
+ return BASE64_PATTERN.test(value) && Buffer.from(value, 'base64').length === ENCRYPTION_KEY_BYTES;
42
+ }
43
+ function parseEncryptionKey(raw) {
44
+ const value = normalize(raw);
45
+ if (value !== undefined && !isValidEncryptionKey(value)) {
46
+ throw new errors_1.ConfigurationError(`Invalid configuration: BFF_TOKEN_ENCRYPTION_KEY must be base64-encoded and exactly ${ENCRYPTION_KEY_BYTES} bytes (AES-256).`);
47
+ }
48
+ return value;
49
+ }
38
50
  function parseConfig(env) {
39
51
  const normalized = Object.fromEntries(exports.REQUIRED_KEYS.map(key => [key, normalize(env[key])]));
40
52
  for (const key of URL_KEYS) {
@@ -44,15 +56,17 @@ function parseConfig(env) {
44
56
  }
45
57
  }
46
58
  const presence = Object.fromEntries(exports.REQUIRED_KEYS.map(key => [key, normalized[key] !== undefined]));
59
+ const tokenEncryptionKey = parseEncryptionKey(env.BFF_TOKEN_ENCRYPTION_KEY);
47
60
  return {
48
61
  forestAuthSecret: normalized.FOREST_AUTH_SECRET,
49
62
  forestEnvSecret: normalized.FOREST_ENV_SECRET,
50
63
  forestServerUrl: normalized.FOREST_SERVER_URL,
51
64
  forestAppUrl: normalized.FOREST_APP_URL,
52
65
  agentUrl: normalized.AGENT_URL,
66
+ tokenEncryptionKey,
53
67
  httpPort: parsePort(env.HTTP_PORT),
54
68
  presence,
55
- hasAllRequired: exports.REQUIRED_KEYS.every(key => presence[key]),
69
+ hasAllRequired: exports.REQUIRED_KEYS.every(key => presence[key]) && tokenEncryptionKey !== undefined,
56
70
  };
57
71
  }
58
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUF5REEsa0NBMkJDO0FBcEZELDZCQUF3QjtBQUV4QiwyREFBMkM7QUFDM0Msc0NBQStDO0FBRWxDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlYLE1BQU0sUUFBUSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxDQUFVLENBQUM7QUFlL0UsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDO0FBQ2hDLE1BQU0sUUFBUSxHQUFHLEtBQUssQ0FBQztBQUN2QixNQUFNLGVBQWUsR0FBRyxPQUFDLENBQUMsR0FBRyxDQUFDLEVBQUUsUUFBUSxFQUFFLFVBQVUsRUFBRSxDQUFDLENBQUM7QUFFeEQsU0FBUyxTQUFTLENBQUMsS0FBeUI7SUFDMUMsT0FBTyxLQUFLLEtBQUssU0FBUyxJQUFJLEtBQUssQ0FBQyxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsS0FBSyxDQUFDO0FBQ3hFLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxHQUF1QjtJQUN4QyxNQUFNLE9BQU8sR0FBRyxHQUFHLEVBQUUsSUFBSSxFQUFFLENBQUM7SUFDNUIsSUFBSSxPQUFPLEtBQUssU0FBUyxJQUFJLE9BQU8sS0FBSyxFQUFFO1FBQUUsT0FBTyxrQkFBZ0IsQ0FBQztJQUVyRSxNQUFNLElBQUksR0FBRyxlQUFlLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQztJQUVuRSxJQUFJLE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLElBQUksSUFBSSxHQUFHLFFBQVEsRUFBRSxDQUFDO1FBQzFDLE1BQU0sSUFBSSwyQkFBa0IsQ0FDMUIscUVBQXFFLFFBQVEsR0FBRyxDQUNqRixDQUFDO0lBQ0osQ0FBQztJQUVELE9BQU8sSUFBSSxDQUFDO0FBQ2QsQ0FBQztBQUVELFNBQVMsU0FBUyxDQUFDLEtBQWE7SUFDOUIsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLElBQUksZUFBZSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxPQUFPLENBQUM7QUFDdkUsQ0FBQztBQUVELFNBQWdCLFdBQVcsQ0FBQyxHQUFzQjtJQUNoRCxNQUFNLFVBQVUsR0FBRyxNQUFNLENBQUMsV0FBVyxDQUNuQyxxQkFBYSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxFQUFFLFNBQVMsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQ1YsQ0FBQztJQUU3QyxLQUFLLE1BQU0sR0FBRyxJQUFJLFFBQVEsRUFBRSxDQUFDO1FBQzNCLE1BQU0sS0FBSyxHQUFHLFVBQVUsQ0FBQyxHQUFHLENBQUMsQ0FBQztRQUU5QixJQUFJLEtBQUssS0FBSyxTQUFTLElBQUksQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztZQUM3QyxNQUFNLElBQUksMkJBQWtCLENBQUMsMEJBQTBCLEdBQUcsK0JBQStCLENBQUMsQ0FBQztRQUM3RixDQUFDO0lBQ0gsQ0FBQztJQUVELE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ2pDLHFCQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLEVBQUUsVUFBVSxDQUFDLEdBQUcsQ0FBQyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQ2hELENBQUM7SUFFakIsT0FBTztRQUNMLGdCQUFnQixFQUFFLFVBQVUsQ0FBQyxrQkFBa0I7UUFDL0MsZUFBZSxFQUFFLFVBQVUsQ0FBQyxpQkFBaUI7UUFDN0MsZUFBZSxFQUFFLFVBQVUsQ0FBQyxpQkFBaUI7UUFDN0MsWUFBWSxFQUFFLFVBQVUsQ0FBQyxjQUFjO1FBQ3ZDLFFBQVEsRUFBRSxVQUFVLENBQUMsU0FBUztRQUM5QixRQUFRLEVBQUUsU0FBUyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUM7UUFDbEMsUUFBUTtRQUNSLGNBQWMsRUFBRSxxQkFBYSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsQ0FBQztLQUMxRCxDQUFDO0FBQ0osQ0FBQyJ9
72
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZW52LWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvZW52LWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUE0RUEsa0NBOEJDO0FBMUdELDZCQUF3QjtBQUV4QiwyREFBMkM7QUFDM0Msc0NBQStDO0FBRWxDLFFBQUEsYUFBYSxHQUFHO0lBQzNCLG9CQUFvQjtJQUNwQixtQkFBbUI7SUFDbkIsbUJBQW1CO0lBQ25CLGdCQUFnQjtJQUNoQixXQUFXO0NBQ0gsQ0FBQztBQUlYLE1BQU0sUUFBUSxHQUFHLENBQUMsbUJBQW1CLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxDQUFVLENBQUM7QUFnQi9FLE1BQU0sZUFBZSxHQUFHLE9BQU8sQ0FBQztBQUNoQyxNQUFNLFFBQVEsR0FBRyxLQUFLLENBQUM7QUFDdkIsTUFBTSxvQkFBb0IsR0FBRyxFQUFFLENBQUM7QUFDaEMsTUFBTSxjQUFjLEdBQUcsd0JBQXdCLENBQUM7QUFDaEQsTUFBTSxlQUFlLEdBQUcsT0FBQyxDQUFDLEdBQUcsQ0FBQyxFQUFFLFFBQVEsRUFBRSxVQUFVLEVBQUUsQ0FBQyxDQUFDO0FBRXhELFNBQVMsU0FBUyxDQUFDLEtBQXlCO0lBQzFDLE9BQU8sS0FBSyxLQUFLLFNBQVMsSUFBSSxLQUFLLENBQUMsSUFBSSxFQUFFLEtBQUssRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQztBQUN4RSxDQUFDO0FBRUQsU0FBUyxTQUFTLENBQUMsR0FBdUI7SUFDeEMsTUFBTSxPQUFPLEdBQUcsR0FBRyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQzVCLElBQUksT0FBTyxLQUFLLFNBQVMsSUFBSSxPQUFPLEtBQUssRUFBRTtRQUFFLE9BQU8sa0JBQWdCLENBQUM7SUFFckUsTUFBTSxJQUFJLEdBQUcsZUFBZSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUM7SUFFbkUsSUFBSSxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxJQUFJLElBQUksR0FBRyxRQUFRLEVBQUUsQ0FBQztRQUMxQyxNQUFNLElBQUksMkJBQWtCLENBQzFCLHFFQUFxRSxRQUFRLEdBQUcsQ0FDakYsQ0FBQztJQUNKLENBQUM7SUFFRCxPQUFPLElBQUksQ0FBQztBQUNkLENBQUM7QUFFRCxTQUFTLFNBQVMsQ0FBQyxLQUFhO0lBQzlCLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLGVBQWUsQ0FBQyxTQUFTLENBQUMsS0FBSyxDQUFDLENBQUMsT0FBTyxDQUFDO0FBQ3ZFLENBQUM7QUFFRCxTQUFTLG9CQUFvQixDQUFDLEtBQWE7SUFDekMsT0FBTyxjQUFjLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLFFBQVEsQ0FBQyxDQUFDLE1BQU0sS0FBSyxvQkFBb0IsQ0FBQztBQUNwRyxDQUFDO0FBRUQsU0FBUyxrQkFBa0IsQ0FBQyxHQUF1QjtJQUNqRCxNQUFNLEtBQUssR0FBRyxTQUFTLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFN0IsSUFBSSxLQUFLLEtBQUssU0FBUyxJQUFJLENBQUMsb0JBQW9CLENBQUMsS0FBSyxDQUFDLEVBQUUsQ0FBQztRQUN4RCxNQUFNLElBQUksMkJBQWtCLENBQzFCLHNGQUFzRixvQkFBb0IsbUJBQW1CLENBQzlILENBQUM7SUFDSixDQUFDO0lBRUQsT0FBTyxLQUFLLENBQUM7QUFDZixDQUFDO0FBRUQsU0FBZ0IsV0FBVyxDQUFDLEdBQXNCO0lBQ2hELE1BQU0sVUFBVSxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQ25DLHFCQUFhLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsQ0FBQyxHQUFHLEVBQUUsU0FBUyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FDVixDQUFDO0lBRTdDLEtBQUssTUFBTSxHQUFHLElBQUksUUFBUSxFQUFFLENBQUM7UUFDM0IsTUFBTSxLQUFLLEdBQUcsVUFBVSxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRTlCLElBQUksS0FBSyxLQUFLLFNBQVMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsRUFBRSxDQUFDO1lBQzdDLE1BQU0sSUFBSSwyQkFBa0IsQ0FBQywwQkFBMEIsR0FBRywrQkFBK0IsQ0FBQyxDQUFDO1FBQzdGLENBQUM7SUFDSCxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsTUFBTSxDQUFDLFdBQVcsQ0FDakMscUJBQWEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsRUFBRSxVQUFVLENBQUMsR0FBRyxDQUFDLEtBQUssU0FBUyxDQUFDLENBQUMsQ0FDaEQsQ0FBQztJQUVqQixNQUFNLGtCQUFrQixHQUFHLGtCQUFrQixDQUFDLEdBQUcsQ0FBQyx3QkFBd0IsQ0FBQyxDQUFDO0lBRTVFLE9BQU87UUFDTCxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsa0JBQWtCO1FBQy9DLGVBQWUsRUFBRSxVQUFVLENBQUMsaUJBQWlCO1FBQzdDLGVBQWUsRUFBRSxVQUFVLENBQUMsaUJBQWlCO1FBQzdDLFlBQVksRUFBRSxVQUFVLENBQUMsY0FBYztRQUN2QyxRQUFRLEVBQUUsVUFBVSxDQUFDLFNBQVM7UUFDOUIsa0JBQWtCO1FBQ2xCLFFBQVEsRUFBRSxTQUFTLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQztRQUNsQyxRQUFRO1FBQ1IsY0FBYyxFQUFFLHFCQUFhLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksa0JBQWtCLEtBQUssU0FBUztLQUM5RixDQUFDO0FBQ0osQ0FBQyJ9
@@ -1,11 +1,13 @@
1
1
  import type { BFFConfig } from '../config/env-config';
2
2
  import type { Logger } from '../ports/logger-port';
3
+ import type { Middleware } from 'koa';
3
4
  import http from 'http';
4
5
  export interface BFFHttpServerOptions {
5
6
  port: number;
6
7
  version: string;
7
8
  config: BFFConfig;
8
9
  logger?: Logger;
10
+ middlewares?: Middleware[];
9
11
  }
10
12
  export default class BFFHttpServer {
11
13
  private readonly app;
@@ -25,6 +25,9 @@ class BFFHttpServer {
25
25
  }
26
26
  await next();
27
27
  });
28
+ for (const middleware of this.options.middlewares ?? []) {
29
+ this.app.use(middleware);
30
+ }
28
31
  }
29
32
  async start() {
30
33
  if (this.server)
@@ -80,4 +83,4 @@ class BFFHttpServer {
80
83
  }
81
84
  }
82
85
  exports.default = BFFHttpServer;
83
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWh0dHAtc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvYmZmLWh0dHAtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBSUEsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUV0QixnRkFBNkQ7QUFTN0QsTUFBcUIsYUFBYTtJQU1oQyxZQUFZLE9BQTZCO1FBRmpDLFdBQU0sR0FBa0IsSUFBSSxDQUFDO1FBR25DLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sSUFBSSxJQUFBLHdCQUFtQixHQUFFLENBQUM7UUFDdEQsSUFBSSxDQUFDLEdBQUcsR0FBRyxJQUFJLGFBQUcsRUFBRSxDQUFDO1FBRXJCLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDL0IsR0FBRyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3RELE1BQU0sSUFBSSxFQUFFLENBQUM7UUFDZixDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDL0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEtBQUssS0FBSyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDOUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO2dCQUN6QyxHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FB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
86
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLWh0dHAtc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2h0dHAvYmZmLWh0dHAtc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBS0EsZ0RBQXdCO0FBQ3hCLDhDQUFzQjtBQUV0QixnRkFBNkQ7QUFVN0QsTUFBcUIsYUFBYTtJQU1oQyxZQUFZLE9BQTZCO1FBRmpDLFdBQU0sR0FBa0IsSUFBSSxDQUFDO1FBR25DLElBQUksQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxNQUFNLEdBQUcsT0FBTyxDQUFDLE1BQU0sSUFBSSxJQUFBLHdCQUFtQixHQUFFLENBQUM7UUFDdEQsSUFBSSxDQUFDLEdBQUcsR0FBRyxJQUFJLGFBQUcsRUFBRSxDQUFDO1FBRXJCLElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDL0IsR0FBRyxDQUFDLEdBQUcsQ0FBQyxzQkFBc0IsRUFBRSxJQUFJLENBQUMsT0FBTyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1lBQ3RELE1BQU0sSUFBSSxFQUFFLENBQUM7UUFDZixDQUFDLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7WUFDL0IsSUFBSSxDQUFDLEdBQUcsQ0FBQyxNQUFNLEtBQUssS0FBSyxJQUFJLEdBQUcsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztnQkFDOUUsTUFBTSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxJQUFJLENBQUMsT0FBTyxDQUFDO2dCQUN6QyxHQUFHLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsR0FB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
package/dist/index.d.ts CHANGED
@@ -7,4 +7,15 @@ export { default as DEFAULT_BFF_PORT } from './defaults';
7
7
  export { default as createConsoleLogger } from './adapters/console-logger';
8
8
  export type { Logger, LoggerLevel } from './ports/logger-port';
9
9
  export { default as version } from './version';
10
+ export { default as ForestServerClient, OAuthExchangeError } from './oauth/forest-server-client';
11
+ export type { ServerTokens, RegisteredClient, ExchangeCodeParams, } from './oauth/forest-server-client';
12
+ export { default as createOAuthRoutes } from './oauth/oauth-routes';
13
+ export { default as createInMemorySessionStore } from './oauth/session-store';
14
+ export type { SessionStore, StoredSession, CreatedSession } from './oauth/session-store';
15
+ export { default as createTokenCipher } from './oauth/token-cipher';
16
+ export { default as ensureFreshServerAccess } from './oauth/session-lifecycle';
17
+ export { issueBffAccessToken, BFF_ACCESS_TOKEN_TYPE } from './oauth/bff-token';
18
+ export type { BffAccessTokenPayload } from './oauth/bff-token';
19
+ export { createPkcePair } from './oauth/pkce';
20
+ export { OAuthRequestError } from './oauth/oauth-error';
10
21
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.version = exports.createConsoleLogger = exports.DEFAULT_BFF_PORT = exports.ConfigurationError = exports.runCli = exports.REQUIRED_KEYS = exports.parseConfig = exports.BFFHttpServer = void 0;
6
+ exports.OAuthRequestError = exports.createPkcePair = exports.BFF_ACCESS_TOKEN_TYPE = exports.issueBffAccessToken = exports.ensureFreshServerAccess = exports.createTokenCipher = exports.createInMemorySessionStore = exports.createOAuthRoutes = exports.OAuthExchangeError = exports.ForestServerClient = exports.version = exports.createConsoleLogger = exports.DEFAULT_BFF_PORT = exports.ConfigurationError = exports.runCli = exports.REQUIRED_KEYS = exports.parseConfig = exports.BFFHttpServer = void 0;
7
7
  var bff_http_server_1 = require("./http/bff-http-server");
8
8
  Object.defineProperty(exports, "BFFHttpServer", { enumerable: true, get: function () { return __importDefault(bff_http_server_1).default; } });
9
9
  var env_config_1 = require("./config/env-config");
@@ -19,4 +19,22 @@ var console_logger_1 = require("./adapters/console-logger");
19
19
  Object.defineProperty(exports, "createConsoleLogger", { enumerable: true, get: function () { return __importDefault(console_logger_1).default; } });
20
20
  var version_1 = require("./version");
21
21
  Object.defineProperty(exports, "version", { enumerable: true, get: function () { return __importDefault(version_1).default; } });
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMERBQWtFO0FBQXpELGlJQUFBLE9BQU8sT0FBaUI7QUFDakMsa0RBQWlFO0FBQXhELHlHQUFBLFdBQVcsT0FBQTtBQUFFLDJHQUFBLGFBQWEsT0FBQTtBQUVuQyx1Q0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFVO0FBQzFCLG1DQUE4QztBQUFyQyw0R0FBQSxrQkFBa0IsT0FBQTtBQUMzQix1Q0FBeUQ7QUFBaEQsNkhBQUEsT0FBTyxPQUFvQjtBQUNwQyw0REFBMkU7QUFBbEUsc0lBQUEsT0FBTyxPQUF1QjtBQUV2QyxxQ0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFXIn0=
22
+ var forest_server_client_1 = require("./oauth/forest-server-client");
23
+ Object.defineProperty(exports, "ForestServerClient", { enumerable: true, get: function () { return __importDefault(forest_server_client_1).default; } });
24
+ Object.defineProperty(exports, "OAuthExchangeError", { enumerable: true, get: function () { return forest_server_client_1.OAuthExchangeError; } });
25
+ var oauth_routes_1 = require("./oauth/oauth-routes");
26
+ Object.defineProperty(exports, "createOAuthRoutes", { enumerable: true, get: function () { return __importDefault(oauth_routes_1).default; } });
27
+ var session_store_1 = require("./oauth/session-store");
28
+ Object.defineProperty(exports, "createInMemorySessionStore", { enumerable: true, get: function () { return __importDefault(session_store_1).default; } });
29
+ var token_cipher_1 = require("./oauth/token-cipher");
30
+ Object.defineProperty(exports, "createTokenCipher", { enumerable: true, get: function () { return __importDefault(token_cipher_1).default; } });
31
+ var session_lifecycle_1 = require("./oauth/session-lifecycle");
32
+ Object.defineProperty(exports, "ensureFreshServerAccess", { enumerable: true, get: function () { return __importDefault(session_lifecycle_1).default; } });
33
+ var bff_token_1 = require("./oauth/bff-token");
34
+ Object.defineProperty(exports, "issueBffAccessToken", { enumerable: true, get: function () { return bff_token_1.issueBffAccessToken; } });
35
+ Object.defineProperty(exports, "BFF_ACCESS_TOKEN_TYPE", { enumerable: true, get: function () { return bff_token_1.BFF_ACCESS_TOKEN_TYPE; } });
36
+ var pkce_1 = require("./oauth/pkce");
37
+ Object.defineProperty(exports, "createPkcePair", { enumerable: true, get: function () { return pkce_1.createPkcePair; } });
38
+ var oauth_error_1 = require("./oauth/oauth-error");
39
+ Object.defineProperty(exports, "OAuthRequestError", { enumerable: true, get: function () { return oauth_error_1.OAuthRequestError; } });
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBQUEsMERBQWtFO0FBQXpELGlJQUFBLE9BQU8sT0FBaUI7QUFDakMsa0RBQWlFO0FBQXhELHlHQUFBLFdBQVcsT0FBQTtBQUFFLDJHQUFBLGFBQWEsT0FBQTtBQUVuQyx1Q0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFVO0FBQzFCLG1DQUE4QztBQUFyQyw0R0FBQSxrQkFBa0IsT0FBQTtBQUMzQix1Q0FBeUQ7QUFBaEQsNkhBQUEsT0FBTyxPQUFvQjtBQUNwQyw0REFBMkU7QUFBbEUsc0lBQUEsT0FBTyxPQUF1QjtBQUV2QyxxQ0FBK0M7QUFBdEMsbUhBQUEsT0FBTyxPQUFXO0FBQzNCLHFFQUFpRztBQUF4RiwySUFBQSxPQUFPLE9BQXNCO0FBQUUsMEhBQUEsa0JBQWtCLE9BQUE7QUFNMUQscURBQW9FO0FBQTNELGtJQUFBLE9BQU8sT0FBcUI7QUFDckMsdURBQThFO0FBQXJFLDRJQUFBLE9BQU8sT0FBOEI7QUFFOUMscURBQW9FO0FBQTNELGtJQUFBLE9BQU8sT0FBcUI7QUFDckMsK0RBQStFO0FBQXRFLDZJQUFBLE9BQU8sT0FBMkI7QUFDM0MsK0NBQStFO0FBQXRFLGdIQUFBLG1CQUFtQixPQUFBO0FBQUUsa0hBQUEscUJBQXFCLE9BQUE7QUFFbkQscUNBQThDO0FBQXJDLHNHQUFBLGNBQWMsT0FBQTtBQUN2QixtREFBd0Q7QUFBL0MsZ0hBQUEsaUJBQWlCLE9BQUEifQ==
@@ -0,0 +1,24 @@
1
+ import type { UserInfo } from '@forestadmin/forestadmin-client';
2
+ export declare const BFF_ACCESS_TOKEN_TYPE = "bff_access";
3
+ export declare const BFF_ACCESS_TOKEN_MAX_EXPIRES_IN: number;
4
+ export interface IssueBffAccessTokenParams {
5
+ sid: string;
6
+ user: UserInfo;
7
+ renderingId: number;
8
+ authSecret: string;
9
+ expiresInSeconds: number;
10
+ }
11
+ export interface BffAccessTokenPayload {
12
+ type: typeof BFF_ACCESS_TOKEN_TYPE;
13
+ sid: string;
14
+ id: number;
15
+ email: string;
16
+ first_name: string;
17
+ last_name: string;
18
+ team: string;
19
+ rendering_id: string;
20
+ permission_level: string;
21
+ tags: Record<string, string>;
22
+ }
23
+ export declare function issueBffAccessToken({ sid, user, renderingId, authSecret, expiresInSeconds, }: IssueBffAccessTokenParams): string;
24
+ //# sourceMappingURL=bff-token.d.ts.map
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BFF_ACCESS_TOKEN_MAX_EXPIRES_IN = exports.BFF_ACCESS_TOKEN_TYPE = void 0;
7
+ exports.issueBffAccessToken = issueBffAccessToken;
8
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
9
+ exports.BFF_ACCESS_TOKEN_TYPE = 'bff_access';
10
+ exports.BFF_ACCESS_TOKEN_MAX_EXPIRES_IN = 15 * 60;
11
+ function issueBffAccessToken({ sid, user, renderingId, authSecret, expiresInSeconds, }) {
12
+ const payload = {
13
+ type: exports.BFF_ACCESS_TOKEN_TYPE,
14
+ sid,
15
+ id: user.id,
16
+ email: user.email,
17
+ first_name: user.firstName,
18
+ last_name: user.lastName,
19
+ team: user.team,
20
+ rendering_id: String(renderingId),
21
+ permission_level: user.permissionLevel,
22
+ tags: user.tags ?? {},
23
+ };
24
+ return jsonwebtoken_1.default.sign(payload, authSecret, {
25
+ algorithm: 'HS256',
26
+ expiresIn: Math.min(expiresInSeconds, exports.BFF_ACCESS_TOKEN_MAX_EXPIRES_IN),
27
+ });
28
+ }
29
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYmZmLXRva2VuLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL29hdXRoL2JmZi10b2tlbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUE0QkEsa0RBd0JDO0FBbERELGdFQUF3QztBQUUzQixRQUFBLHFCQUFxQixHQUFHLFlBQVksQ0FBQztBQUNyQyxRQUFBLCtCQUErQixHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUF1QnZELFNBQWdCLG1CQUFtQixDQUFDLEVBQ2xDLEdBQUcsRUFDSCxJQUFJLEVBQ0osV0FBVyxFQUNYLFVBQVUsRUFDVixnQkFBZ0IsR0FDVTtJQUMxQixNQUFNLE9BQU8sR0FBMEI7UUFDckMsSUFBSSxFQUFFLDZCQUFxQjtRQUMzQixHQUFHO1FBQ0gsRUFBRSxFQUFFLElBQUksQ0FBQyxFQUFFO1FBQ1gsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLO1FBQ2pCLFVBQVUsRUFBRSxJQUFJLENBQUMsU0FBUztRQUMxQixTQUFTLEVBQUUsSUFBSSxDQUFDLFFBQVE7UUFDeEIsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJO1FBQ2YsWUFBWSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUM7UUFDakMsZ0JBQWdCLEVBQUUsSUFBSSxDQUFDLGVBQWU7UUFDdEMsSUFBSSxFQUFFLElBQUksQ0FBQyxJQUFJLElBQUksRUFBRTtLQUN0QixDQUFDO0lBRUYsT0FBTyxzQkFBWSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsVUFBVSxFQUFFO1FBQzVDLFNBQVMsRUFBRSxPQUFPO1FBQ2xCLFNBQVMsRUFBRSxJQUFJLENBQUMsR0FBRyxDQUFDLGdCQUFnQixFQUFFLHVDQUErQixDQUFDO0tBQ3ZFLENBQUMsQ0FBQztBQUNMLENBQUMifQ==
@@ -0,0 +1,40 @@
1
+ import type { UserInfo } from '@forestadmin/forestadmin-client';
2
+ import OAuthExchangeError from './oauth-exchange-error';
3
+ export { OAuthExchangeError };
4
+ export interface RegisteredClient {
5
+ client_id: string;
6
+ client_name?: string;
7
+ redirect_uris?: string[];
8
+ grant_types?: string[];
9
+ }
10
+ export interface ServerTokens {
11
+ saasAccessToken: string;
12
+ saasRefreshToken: string;
13
+ renderingId: number;
14
+ expiresAt: number;
15
+ }
16
+ export interface ExchangeCodeParams {
17
+ code: string;
18
+ codeVerifier: string;
19
+ redirectUri: string;
20
+ clientId: string;
21
+ }
22
+ export interface ForestServerClientOptions {
23
+ forestServerUrl: string;
24
+ envSecret: string;
25
+ }
26
+ export default class ForestServerClient {
27
+ private readonly forestServerUrl;
28
+ private readonly envSecret;
29
+ private readonly forestClient;
30
+ constructor({ forestServerUrl, envSecret }: ForestServerClientOptions);
31
+ private url;
32
+ fetchEnvironmentId(): Promise<number>;
33
+ getRegisteredClient(clientId: string): Promise<RegisteredClient | undefined>;
34
+ exchangeCode({ code, codeVerifier, redirectUri, clientId, }: ExchangeCodeParams): Promise<ServerTokens>;
35
+ refreshServerToken(saasRefreshToken: string): Promise<ServerTokens>;
36
+ private postToken;
37
+ private static toServerTokens;
38
+ getUserInfo(renderingId: number, saasAccessToken: string): Promise<UserInfo>;
39
+ }
40
+ //# sourceMappingURL=forest-server-client.d.ts.map
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.OAuthExchangeError = void 0;
7
+ const forestadmin_client_1 = __importDefault(require("@forestadmin/forestadmin-client"));
8
+ const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
9
+ const oauth_exchange_error_1 = __importDefault(require("./oauth-exchange-error"));
10
+ exports.OAuthExchangeError = oauth_exchange_error_1.default;
11
+ const DEFAULT_HEADERS = { 'Content-Type': 'application/json' };
12
+ const REQUEST_TIMEOUT_MS = 60000;
13
+ function fetchWithTimeout(url, init) {
14
+ return fetch(url, { ...init, signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS) });
15
+ }
16
+ class ForestServerClient {
17
+ constructor({ forestServerUrl, envSecret }) {
18
+ this.forestServerUrl = forestServerUrl;
19
+ this.envSecret = envSecret;
20
+ this.forestClient = (0, forestadmin_client_1.default)({ forestServerUrl, envSecret });
21
+ }
22
+ url(path) {
23
+ return new URL(path, this.forestServerUrl).toString();
24
+ }
25
+ async fetchEnvironmentId() {
26
+ const response = await fetchWithTimeout(this.url('/liana/environment'), {
27
+ method: 'GET',
28
+ headers: { ...DEFAULT_HEADERS, 'forest-secret-key': this.envSecret },
29
+ });
30
+ if (!response.ok) {
31
+ throw new Error(`Failed to fetch environment: ${response.status} ${response.statusText}`);
32
+ }
33
+ const body = (await response.json());
34
+ const id = body.data?.id;
35
+ if (typeof id !== 'number' && (typeof id !== 'string' || !/^\d+$/.test(id))) {
36
+ throw new Error('Failed to parse environment id from the Forest server response');
37
+ }
38
+ const environmentId = Number(id);
39
+ if (!Number.isInteger(environmentId) || environmentId <= 0) {
40
+ throw new Error('Failed to parse environment id from the Forest server response');
41
+ }
42
+ return environmentId;
43
+ }
44
+ async getRegisteredClient(clientId) {
45
+ const response = await fetchWithTimeout(this.url(`/oauth/register/${encodeURIComponent(clientId)}`), {
46
+ method: 'GET',
47
+ headers: DEFAULT_HEADERS,
48
+ });
49
+ if (response.status === 404)
50
+ return undefined;
51
+ if (!response.ok) {
52
+ throw new Error(`Failed to fetch registered client: ${response.status} ${response.statusText}`);
53
+ }
54
+ return response.json();
55
+ }
56
+ async exchangeCode({ code, codeVerifier, redirectUri, clientId, }) {
57
+ return this.postToken({
58
+ grant_type: 'authorization_code',
59
+ code,
60
+ redirect_uri: redirectUri,
61
+ client_id: clientId,
62
+ code_verifier: codeVerifier,
63
+ }, true);
64
+ }
65
+ async refreshServerToken(saasRefreshToken) {
66
+ return this.postToken({ grant_type: 'refresh_token', refresh_token: saasRefreshToken }, false);
67
+ }
68
+ async postToken(payload, isInitialExchange) {
69
+ const response = await fetchWithTimeout(this.url('/oauth/token'), {
70
+ method: 'POST',
71
+ headers: { ...DEFAULT_HEADERS, 'forest-secret-key': this.envSecret },
72
+ body: JSON.stringify(payload),
73
+ });
74
+ if (!response.ok) {
75
+ const errorBody = (await response.json().catch(() => ({})));
76
+ throw new oauth_exchange_error_1.default(errorBody.error || 'server_error', errorBody.error_description || 'Failed to exchange token with the Forest server');
77
+ }
78
+ const tokens = (await response.json());
79
+ const saasRefreshToken = tokens.refresh_token ?? payload.refresh_token;
80
+ if (saasRefreshToken === undefined) {
81
+ throw new oauth_exchange_error_1.default('invalid_grant', 'The Forest server did not return a refresh token');
82
+ }
83
+ return ForestServerClient.toServerTokens(tokens.access_token, saasRefreshToken, isInitialExchange);
84
+ }
85
+ static toServerTokens(saasAccessToken, saasRefreshToken, requireRenderingId) {
86
+ const decoded = jsonwebtoken_1.default.decode(saasAccessToken);
87
+ const decodedRenderingId = Number(decoded?.meta?.renderingId);
88
+ const hasRenderingId = Number.isInteger(decodedRenderingId);
89
+ if (requireRenderingId && !hasRenderingId) {
90
+ throw new Error('Failed to decode renderingId from the Forest server access token');
91
+ }
92
+ return {
93
+ saasAccessToken,
94
+ saasRefreshToken,
95
+ renderingId: hasRenderingId ? decodedRenderingId : 0,
96
+ expiresAt: decoded?.exp ?? 0,
97
+ };
98
+ }
99
+ async getUserInfo(renderingId, saasAccessToken) {
100
+ return this.forestClient.authService.getUserInfo(renderingId, saasAccessToken);
101
+ }
102
+ }
103
+ exports.default = ForestServerClient;
104
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZm9yZXN0LXNlcnZlci1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvZm9yZXN0LXNlcnZlci1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7O0FBRUEseUZBQXNFO0FBQ3RFLGdFQUF3QztBQUV4QyxrRkFBd0Q7QUFFL0MsNkJBRkYsOEJBQWtCLENBRUU7QUE0QjNCLE1BQU0sZUFBZSxHQUFHLEVBQUUsY0FBYyxFQUFFLGtCQUFrQixFQUFXLENBQUM7QUFDeEUsTUFBTSxrQkFBa0IsR0FBRyxLQUFNLENBQUM7QUFFbEMsU0FBUyxnQkFBZ0IsQ0FBQyxHQUFXLEVBQUUsSUFBaUI7SUFDdEQsT0FBTyxLQUFLLENBQUMsR0FBRyxFQUFFLEVBQUUsR0FBRyxJQUFJLEVBQUUsTUFBTSxFQUFFLFdBQVcsQ0FBQyxPQUFPLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLENBQUM7QUFDbEYsQ0FBQztBQUVELE1BQXFCLGtCQUFrQjtJQUtyQyxZQUFZLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBNkI7UUFDbkUsSUFBSSxDQUFDLGVBQWUsR0FBRyxlQUFlLENBQUM7UUFDdkMsSUFBSSxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUM7UUFDM0IsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFBLDRCQUF1QixFQUFDLEVBQUUsZUFBZSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDOUUsQ0FBQztJQUVPLEdBQUcsQ0FBQyxJQUFZO1FBQ3RCLE9BQU8sSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLElBQUksQ0FBQyxlQUFlLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztJQUN4RCxDQUFDO0lBRUQsS0FBSyxDQUFDLGtCQUFrQjtRQUN0QixNQUFNLFFBQVEsR0FBRyxNQUFNLGdCQUFnQixDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsb0JBQW9CLENBQUMsRUFBRTtZQUN0RSxNQUFNLEVBQUUsS0FBSztZQUNiLE9B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
@@ -0,0 +1,19 @@
1
+ export declare class OAuthRequestError extends Error {
2
+ readonly status: number;
3
+ readonly type: string;
4
+ constructor(status: number, type: string, message: string);
5
+ }
6
+ export interface OAuthErrorBody {
7
+ error: string;
8
+ error_description: string;
9
+ }
10
+ export declare function toErrorBody(error: OAuthRequestError): OAuthErrorBody;
11
+ export declare function invalidRequest(message: string): OAuthRequestError;
12
+ export declare function invalidClient(message: string): OAuthRequestError;
13
+ export declare function invalidGrant(message: string): OAuthRequestError;
14
+ export declare function unsupportedGrantType(message: string): OAuthRequestError;
15
+ export declare function forestIdentityNotAllowed(message: string): OAuthRequestError;
16
+ export declare function sessionExpired(message: string): OAuthRequestError;
17
+ export declare function sessionInvalidated(message: string): OAuthRequestError;
18
+ export declare function serverError(message: string): OAuthRequestError;
19
+ //# sourceMappingURL=oauth-error.d.ts.map
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OAuthRequestError = void 0;
4
+ exports.toErrorBody = toErrorBody;
5
+ exports.invalidRequest = invalidRequest;
6
+ exports.invalidClient = invalidClient;
7
+ exports.invalidGrant = invalidGrant;
8
+ exports.unsupportedGrantType = unsupportedGrantType;
9
+ exports.forestIdentityNotAllowed = forestIdentityNotAllowed;
10
+ exports.sessionExpired = sessionExpired;
11
+ exports.sessionInvalidated = sessionInvalidated;
12
+ exports.serverError = serverError;
13
+ class OAuthRequestError extends Error {
14
+ constructor(status, type, message) {
15
+ super(message);
16
+ this.name = 'OAuthRequestError';
17
+ this.status = status;
18
+ this.type = type;
19
+ }
20
+ }
21
+ exports.OAuthRequestError = OAuthRequestError;
22
+ function toErrorBody(error) {
23
+ return { error: error.type, error_description: error.message };
24
+ }
25
+ function invalidRequest(message) {
26
+ return new OAuthRequestError(400, 'invalid_request', message);
27
+ }
28
+ function invalidClient(message) {
29
+ return new OAuthRequestError(400, 'invalid_client', message);
30
+ }
31
+ function invalidGrant(message) {
32
+ return new OAuthRequestError(400, 'invalid_grant', message);
33
+ }
34
+ function unsupportedGrantType(message) {
35
+ return new OAuthRequestError(400, 'unsupported_grant_type', message);
36
+ }
37
+ function forestIdentityNotAllowed(message) {
38
+ return new OAuthRequestError(403, 'forest_identity_not_allowed', message);
39
+ }
40
+ function sessionExpired(message) {
41
+ return new OAuthRequestError(401, 'session_expired', message);
42
+ }
43
+ function sessionInvalidated(message) {
44
+ return new OAuthRequestError(401, 'session_invalidated', message);
45
+ }
46
+ function serverError(message) {
47
+ return new OAuthRequestError(502, 'server_error', message);
48
+ }
49
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2F1dGgtZXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvb2F1dGgtZXJyb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBaUJBLGtDQUVDO0FBRUQsd0NBRUM7QUFFRCxzQ0FFQztBQUVELG9DQUVDO0FBRUQsb0RBRUM7QUFFRCw0REFFQztBQUVELHdDQUVDO0FBRUQsZ0RBRUM7QUFFRCxrQ0FFQztBQW5ERCxNQUFhLGlCQUFrQixTQUFRLEtBQUs7SUFJMUMsWUFBWSxNQUFjLEVBQUUsSUFBWSxFQUFFLE9BQWU7UUFDdkQsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2YsSUFBSSxDQUFDLElBQUksR0FBRyxtQkFBbUIsQ0FBQztRQUNoQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQztRQUNyQixJQUFJLENBQUMsSUFBSSxHQUFHLElBQUksQ0FBQztJQUNuQixDQUFDO0NBQ0Y7QUFWRCw4Q0FVQztBQU9ELFNBQWdCLFdBQVcsQ0FBQyxLQUF3QjtJQUNsRCxPQUFPLEVBQUUsS0FBSyxFQUFFLEtBQUssQ0FBQyxJQUFJLEVBQUUsaUJBQWlCLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO0FBQ2pFLENBQUM7QUFFRCxTQUFnQixjQUFjLENBQUMsT0FBZTtJQUM1QyxPQUFPLElBQUksaUJBQWlCLENBQUMsR0FBRyxFQUFFLGlCQUFpQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQ2hFLENBQUM7QUFFRCxTQUFnQixhQUFhLENBQUMsT0FBZTtJQUMzQyxPQUFPLElBQUksaUJBQWlCLENBQUMsR0FBRyxFQUFFLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQy9ELENBQUM7QUFFRCxTQUFnQixZQUFZLENBQUMsT0FBZTtJQUMxQyxPQUFPLElBQUksaUJBQWlCLENBQUMsR0FBRyxFQUFFLGVBQWUsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUM5RCxDQUFDO0FBRUQsU0FBZ0Isb0JBQW9CLENBQUMsT0FBZTtJQUNsRCxPQUFPLElBQUksaUJBQWlCLENBQUMsR0FBRyxFQUFFLHdCQUF3QixFQUFFLE9BQU8sQ0FBQyxDQUFDO0FBQ3ZFLENBQUM7QUFFRCxTQUFnQix3QkFBd0IsQ0FBQyxPQUFlO0lBQ3RELE9BQU8sSUFBSSxpQkFBaUIsQ0FBQyxHQUFHLEVBQUUsNkJBQTZCLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDNUUsQ0FBQztBQUVELFNBQWdCLGNBQWMsQ0FBQyxPQUFlO0lBQzVDLE9BQU8sSUFBSSxpQkFBaUIsQ0FBQyxHQUFHLEVBQUUsaUJBQWlCLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDaEUsQ0FBQztBQUVELFNBQWdCLGtCQUFrQixDQUFDLE9BQWU7SUFDaEQsT0FBTyxJQUFJLGlCQUFpQixDQUFDLEdBQUcsRUFBRSxxQkFBcUIsRUFBRSxPQUFPLENBQUMsQ0FBQztBQUNwRSxDQUFDO0FBRUQsU0FBZ0IsV0FBVyxDQUFDLE9BQWU7SUFDekMsT0FBTyxJQUFJLGlCQUFpQixDQUFDLEdBQUcsRUFBRSxjQUFjLEVBQUUsT0FBTyxDQUFDLENBQUM7QUFDN0QsQ0FBQyJ9
@@ -0,0 +1,5 @@
1
+ export default class OAuthExchangeError extends Error {
2
+ readonly error: string;
3
+ constructor(error: string, description: string);
4
+ }
5
+ //# sourceMappingURL=oauth-exchange-error.d.ts.map
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class OAuthExchangeError extends Error {
4
+ constructor(error, description) {
5
+ super(description);
6
+ this.name = 'OAuthExchangeError';
7
+ this.error = error;
8
+ }
9
+ }
10
+ exports.default = OAuthExchangeError;
11
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib2F1dGgtZXhjaGFuZ2UtZXJyb3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvb2F1dGgvb2F1dGgtZXhjaGFuZ2UtZXJyb3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxNQUFxQixrQkFBbUIsU0FBUSxLQUFLO0lBR25ELFlBQVksS0FBYSxFQUFFLFdBQW1CO1FBQzVDLEtBQUssQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUNuQixJQUFJLENBQUMsSUFBSSxHQUFHLG9CQUFvQixDQUFDO1FBQ2pDLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDO0lBQ3JCLENBQUM7Q0FDRjtBQVJELHFDQVFDIn0=
@@ -0,0 +1,14 @@
1
+ import type ForestServerClient from './forest-server-client';
2
+ import type { SessionStore } from './session-store';
3
+ import type { Logger } from '../ports/logger-port';
4
+ import type { Middleware } from 'koa';
5
+ export interface OAuthRoutesOptions {
6
+ serverClient: ForestServerClient;
7
+ sessionStore: SessionStore;
8
+ forestAppUrl: string;
9
+ authSecret: string;
10
+ environmentId: number;
11
+ logger: Logger;
12
+ }
13
+ export default function createOAuthRoutes(options: OAuthRoutesOptions): Middleware;
14
+ //# sourceMappingURL=oauth-routes.d.ts.map