diff --git a/packages/sync-server/src/load-config.js b/packages/sync-server/src/load-config.js index 4de7763172..43794ad52f 100644 --- a/packages/sync-server/src/load-config.js +++ b/packages/sync-server/src/load-config.js @@ -35,7 +35,28 @@ convict.addFormat({ validate(val) { if (val === 'never' || val === 'openid-provider') return; if (typeof val === 'number' && Number.isFinite(val) && val >= 0) return; - throw new Error(`Invalid token_expiration value: ${val}`); + + // Handle string values that can be converted to numbers (from env vars) + if (typeof val === 'string') { + const numVal = Number(val); + if (Number.isFinite(numVal) && numVal >= 0) return; + } + + throw new Error( + `Invalid token_expiration value: ${val}: value was "${val}"`, + ); + }, + coerce(val) { + if (val === 'never' || val === 'openid-provider') return val; + if (typeof val === 'number') return val; + + // Convert string values to numbers for environment variables + if (typeof val === 'string') { + const numVal = Number(val); + if (Number.isFinite(numVal) && numVal >= 0) return numVal; + } + + return val; // Let validate() handle invalid values }, }); diff --git a/packages/sync-server/src/load-config.test.js b/packages/sync-server/src/load-config.test.js new file mode 100644 index 0000000000..0c85d84ac1 --- /dev/null +++ b/packages/sync-server/src/load-config.test.js @@ -0,0 +1,112 @@ +import convict from 'convict'; +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; + +// Import the custom format +import './load-config.js'; + +describe('tokenExpiration format', () => { + let originalEnv; + + beforeEach(() => { + originalEnv = { ...process.env }; + }); + + afterEach(() => { + process.env = originalEnv; + }); + + it('should accept string numbers from environment variables', () => { + // Test string number + process.env.TEST_TOKEN_EXPIRATION = '86400'; + const testSchema = convict({ + token_expiration: { + format: 'tokenExpiration', + default: 'never', + env: 'TEST_TOKEN_EXPIRATION', + }, + }); + expect(() => testSchema.validate()).not.toThrow(); + expect(testSchema.get('token_expiration')).toBe(86400); + expect(typeof testSchema.get('token_expiration')).toBe('number'); + }); + + it('should accept different string numbers', () => { + const testSchema = convict({ + token_expiration: { + format: 'tokenExpiration', + default: 'never', + env: 'TEST_TOKEN_EXPIRATION', + }, + }); + + // Test different string numbers + const testCases = ['3600', '7200', '0']; + + for (const testValue of testCases) { + process.env.TEST_TOKEN_EXPIRATION = testValue; + testSchema.load({}); + expect(() => testSchema.validate()).not.toThrow(); + expect(testSchema.get('token_expiration')).toBe(Number(testValue)); + expect(typeof testSchema.get('token_expiration')).toBe('number'); + } + }); + + it('should accept special string values', () => { + const testSchema = convict({ + token_expiration: { + format: 'tokenExpiration', + default: 'never', + env: 'TEST_TOKEN_EXPIRATION', + }, + }); + + // Test 'never' value + process.env.TEST_TOKEN_EXPIRATION = 'never'; + testSchema.load({}); + expect(() => testSchema.validate()).not.toThrow(); + expect(testSchema.get('token_expiration')).toBe('never'); + + // Test 'openid-provider' value + process.env.TEST_TOKEN_EXPIRATION = 'openid-provider'; + testSchema.load({}); + expect(() => testSchema.validate()).not.toThrow(); + expect(testSchema.get('token_expiration')).toBe('openid-provider'); + }); + + it('should accept numeric values directly', () => { + const testSchema = convict({ + token_expiration: { + format: 'tokenExpiration', + default: 'never', + }, + }); + + testSchema.set('token_expiration', 86400); + expect(() => testSchema.validate()).not.toThrow(); + expect(testSchema.get('token_expiration')).toBe(86400); + }); + + it('should reject invalid string values', () => { + const testSchema = convict({ + token_expiration: { + format: 'tokenExpiration', + default: 'never', + env: 'TEST_TOKEN_EXPIRATION', + }, + }); + + // Test invalid string + process.env.TEST_TOKEN_EXPIRATION = 'invalid'; + testSchema.load({}); + expect(() => testSchema.validate()).toThrow( + /Invalid token_expiration value/, + ); + + // Test negative number as string + process.env.TEST_TOKEN_EXPIRATION = '-100'; + testSchema.load({}); + expect(() => testSchema.validate()).toThrow( + /Invalid token_expiration value/, + ); + }); +}); diff --git a/upcoming-release-notes/5782.md b/upcoming-release-notes/5782.md new file mode 100644 index 0000000000..cd183ccada --- /dev/null +++ b/upcoming-release-notes/5782.md @@ -0,0 +1,7 @@ +--- +category: Bugfix +authors: [MatissJanis] +--- + +Fix token expiration parsing to accept numeric strings and validate special string formats. +