Skip to content

Commit 9d29626

Browse files
committed
fix(auth): use supported Vite token expiry injection
1 parent 5f87d03 commit 9d29626

6 files changed

Lines changed: 59 additions & 25 deletions

File tree

.secrets.baseline

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2567,23 +2567,23 @@
25672567
"filename": "src/frontend/src/constants/constants.ts",
25682568
"hashed_secret": "19a2fbd0dd38b4097f419c962342ef5e109eab07",
25692569
"is_verified": false,
2570-
"line_number": 767,
2570+
"line_number": 764,
25712571
"is_secret": false
25722572
},
25732573
{
25742574
"type": "Secret Keyword",
25752575
"filename": "src/frontend/src/constants/constants.ts",
25762576
"hashed_secret": "3806954324550e26ef5de85d007f1746825a073c",
25772577
"is_verified": false,
2578-
"line_number": 768,
2578+
"line_number": 765,
25792579
"is_secret": false
25802580
},
25812581
{
25822582
"type": "Secret Keyword",
25832583
"filename": "src/frontend/src/constants/constants.ts",
25842584
"hashed_secret": "c04f8fbf55c9096907a982750b1c6b0e4c1dd658",
25852585
"is_verified": false,
2586-
"line_number": 952,
2586+
"line_number": 955,
25872587
"is_secret": false
25882588
}
25892589
],
@@ -7242,5 +7242,5 @@
72427242
}
72437243
]
72447244
},
7245-
"generated_at": "2026-07-24T14:19:19Z"
7245+
"generated_at": "2026-07-24T21:27:29Z"
72467246
}

docs/docs/Develop/api-keys-and-authentication.mdx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,17 +451,20 @@ LANGFLOW_CORS_ALLOW_METHODS=["GET","POST","PUT"]
451451
```
452452
:::
453453

454-
### LANGFLOW_ACCESS_* {#session-cookie-hardening}
454+
### LANGFLOW_ACCESS_* and LANGFLOW_REFRESH_* {#session-cookie-hardening}
455455

456-
For a shared or public deployment served over HTTPS, harden the access-token cookie. These default to permissive values for local HTTP development and for the current frontend, which reads the access token in JavaScript.
456+
For a shared or public deployment served over HTTPS, harden the session cookies. These default to values that support local HTTP development and same-site deployments. The current frontend reads the access token in JavaScript, while the refresh token remains `HttpOnly`.
457457

458-
The refresh-token cookie is `HttpOnly` + `Secure` + `SameSite` by default.
458+
Set both `LANGFLOW_ACCESS_SECURE` and `LANGFLOW_REFRESH_SECURE` to `True` for HTTPS deployments. Cross-site HTTPS deployments must also set `LANGFLOW_REFRESH_SAME_SITE=none`.
459459

460460
| Variable | Format | Default | Description |
461461
|----------|--------|---------|-------------|
462462
| `LANGFLOW_ACCESS_SECURE` | Boolean | `False` | When `true`, the `access_token_lf` cookie is sent only over HTTPS. Recommended `true` for any HTTPS deployment. |
463463
| `LANGFLOW_ACCESS_HTTPONLY` | Boolean | `False` | When `true`, the `access_token_lf` cookie is not readable by JavaScript. The default is `false` because the bundled frontend currently reads this cookie in JavaScript. |
464464
| `LANGFLOW_ACCESS_SAME_SITE` | String | `lax` | The `SameSite` attribute of the access-token cookie (`lax`, `strict`, or `none`). |
465+
| `LANGFLOW_REFRESH_SECURE` | Boolean | `False` | When `true`, the `refresh_token_lf` cookie is sent only over HTTPS. Set this to `true` for any HTTPS deployment. |
466+
| `LANGFLOW_REFRESH_HTTPONLY` | Boolean | `True` | When `true`, the `refresh_token_lf` cookie is not readable by JavaScript. |
467+
| `LANGFLOW_REFRESH_SAME_SITE` | String | `lax` | The `SameSite` attribute of the refresh-token cookie (`lax`, `strict`, or `none`). |
465468

466469
### LANGFLOW_RATE_LIMIT_* {#login-rate-limiting}
467470

src/frontend/src/constants/__tests__/auth-token-expiration.test.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import {
2+
ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY,
3+
createAccessTokenExpireSecondsDefinition,
4+
} from "../../../vite-env-definitions";
5+
16
describe("authentication token refresh timing", () => {
27
const originalAccessTokenExpiry = process.env.ACCESS_TOKEN_EXPIRE_SECONDS;
38

@@ -11,7 +16,11 @@ describe("authentication token refresh timing", () => {
1116
});
1217

1318
it("defaults to refreshing 10% before the one-hour backend expiry", async () => {
14-
delete process.env.ACCESS_TOKEN_EXPIRE_SECONDS;
19+
const definitions = createAccessTokenExpireSecondsDefinition();
20+
expect(definitions[ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY]).toBe("3600");
21+
process.env.ACCESS_TOKEN_EXPIRE_SECONDS = String(
22+
JSON.parse(definitions[ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY]),
23+
);
1524
jest.resetModules();
1625

1726
const { LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV } = await import(
@@ -22,7 +31,11 @@ describe("authentication token refresh timing", () => {
2231
});
2332

2433
it("refreshes 10% before a configured expiry", async () => {
25-
process.env.ACCESS_TOKEN_EXPIRE_SECONDS = "7200";
34+
const definitions = createAccessTokenExpireSecondsDefinition("7200");
35+
expect(definitions[ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY]).toBe('"7200"');
36+
process.env.ACCESS_TOKEN_EXPIRE_SECONDS = String(
37+
JSON.parse(definitions[ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY]),
38+
);
2639
jest.resetModules();
2740

2841
const { LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV } = await import(

src/frontend/src/constants/constants.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,14 @@ export const SLIDING_TRANSITION_MS = 300;
1313

1414
const getEnvVar = <T = string | undefined>(
1515
key: string,
16+
viteValue: T | undefined,
1617
defaultValue?: T,
1718
): T | undefined => {
18-
if (typeof process !== "undefined" && process.env) {
19-
return (process.env[key] as T) ?? defaultValue;
20-
}
21-
try {
22-
const value = new Function(`return import.meta.env?.${key}`)() as T;
23-
return value ?? defaultValue;
24-
} catch {
25-
return defaultValue;
26-
}
19+
const processValue =
20+
typeof process !== "undefined" && process.env
21+
? (process.env[key] as T | undefined)
22+
: undefined;
23+
return processValue ?? viteValue ?? defaultValue;
2724
};
2825

2926
/**
@@ -881,9 +878,15 @@ export const LANGFLOW_AUTO_LOGIN_OPTION = "auto_login_lf";
881878
export const LANGFLOW_REFRESH_TOKEN = "refresh_token_lf";
882879

883880
export const LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS = 60 * 60 - 60 * 60 * 0.1;
881+
const configuredAccessTokenExpireSeconds = Number(
882+
getEnvVar<string | number>(
883+
"ACCESS_TOKEN_EXPIRE_SECONDS",
884+
import.meta.env.ACCESS_TOKEN_EXPIRE_SECONDS,
885+
60 * 60,
886+
),
887+
);
884888
export const LANGFLOW_ACCESS_TOKEN_EXPIRE_SECONDS_ENV =
885-
Number(getEnvVar("ACCESS_TOKEN_EXPIRE_SECONDS", 60 * 60)) -
886-
Number(getEnvVar("ACCESS_TOKEN_EXPIRE_SECONDS", 60 * 60)) * 0.1;
889+
configuredAccessTokenExpireSeconds - configuredAccessTokenExpireSeconds * 0.1;
887890
export const TEXT_FIELD_TYPES: string[] = ["str", "SecretStr"];
888891
export const NODE_WIDTH = 384;
889892
export const NODE_HEIGHT = NODE_WIDTH * 3;
@@ -958,9 +961,12 @@ export const POLLING_MESSAGES = {
958961

959962
export const BUILD_POLLING_INTERVAL = 25;
960963

964+
const autoLoginEnv = getEnvVar<string | boolean>(
965+
"LANGFLOW_AUTO_LOGIN",
966+
import.meta.env.LANGFLOW_AUTO_LOGIN,
967+
);
961968
export const IS_AUTO_LOGIN =
962-
!getEnvVar("LANGFLOW_AUTO_LOGIN") ||
963-
String(getEnvVar("LANGFLOW_AUTO_LOGIN"))?.toLowerCase() !== "false";
969+
!autoLoginEnv || String(autoLoginEnv).toLowerCase() !== "false";
964970

965971
export const AUTO_LOGIN_RETRY_DELAY = 2000;
966972
export const AUTO_LOGIN_MAX_RETRY_DELAY = 60000;
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
export const ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY =
2+
"import.meta.env.ACCESS_TOKEN_EXPIRE_SECONDS";
3+
export const DEFAULT_ACCESS_TOKEN_EXPIRE_SECONDS = 60 * 60;
4+
5+
export const createAccessTokenExpireSecondsDefinition = (
6+
configuredValue?: string,
7+
) => ({
8+
[ACCESS_TOKEN_EXPIRE_SECONDS_ENV_KEY]: JSON.stringify(
9+
configuredValue ?? DEFAULT_ACCESS_TOKEN_EXPIRE_SECONDS,
10+
),
11+
});

src/frontend/vite.config.mts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
PORT,
1212
PROXY_TARGET,
1313
} from "./src/customization/config-constants";
14+
import { createAccessTokenExpireSecondsDefinition } from "./vite-env-definitions";
1415

1516
export default defineConfig(({ mode }) => {
1617
const env = loadEnv(mode, process.cwd(), "");
@@ -44,12 +45,12 @@ export default defineConfig(({ mode }) => {
4445
outDir: "build",
4546
},
4647
define: {
48+
...createAccessTokenExpireSecondsDefinition(
49+
envLangflow.ACCESS_TOKEN_EXPIRE_SECONDS,
50+
),
4751
"import.meta.env.BACKEND_URL": JSON.stringify(
4852
envLangflow.BACKEND_URL ?? "http://localhost:7860",
4953
),
50-
"import.meta.env.ACCESS_TOKEN_EXPIRE_SECONDS": JSON.stringify(
51-
envLangflow.ACCESS_TOKEN_EXPIRE_SECONDS ?? 60 * 60,
52-
),
5354
"import.meta.env.CI": JSON.stringify(envLangflow.CI ?? false),
5455
"import.meta.env.LANGFLOW_AUTO_LOGIN": JSON.stringify(
5556
envLangflow.LANGFLOW_AUTO_LOGIN ?? true,

0 commit comments

Comments
 (0)