diff --git a/backend/modules/oidc/providerConfig.js b/backend/modules/oidc/providerConfig.js index 5bcb096..69b0751 100644 --- a/backend/modules/oidc/providerConfig.js +++ b/backend/modules/oidc/providerConfig.js @@ -8,6 +8,9 @@ function parseCommaSeparated(value) { function loadProvidersFromEnv() { if (process.env.OIDC_ENABLED !== 'true') { + console.log( + 'OIDC is disabled. Set OIDC_ENABLED=true to enable SSO authentication.' + ); return []; } @@ -31,17 +34,24 @@ function loadProvidersFromEnv() { ), }; - if ( - !provider.slug || - !provider.name || - !provider.issuer || - !provider.clientId || - !provider.clientSecret - ) { + const missingFields = []; + if (!provider.slug) missingFields.push(`OIDC_PROVIDER_${i}_SLUG`); + if (!provider.name) missingFields.push(`OIDC_PROVIDER_${i}_NAME`); + if (!provider.issuer) missingFields.push(`OIDC_PROVIDER_${i}_ISSUER`); + if (!provider.clientId) + missingFields.push(`OIDC_PROVIDER_${i}_CLIENT_ID`); + if (!provider.clientSecret) + missingFields.push(`OIDC_PROVIDER_${i}_CLIENT_SECRET`); + + if (missingFields.length > 0) { + console.warn( + `Skipping OIDC provider ${i} due to missing required fields: ${missingFields.join(', ')}` + ); i++; continue; } + console.log(`Loaded OIDC provider ${i}: ${provider.name}`); providers.push(provider); i++; } @@ -60,13 +70,28 @@ function loadProvidersFromEnv() { ), }; - if (!provider.issuer || !provider.clientId || !provider.clientSecret) { + const missingFields = []; + if (!provider.issuer) missingFields.push('OIDC_ISSUER_URL'); + if (!provider.clientId) missingFields.push('OIDC_CLIENT_ID'); + if (!provider.clientSecret) missingFields.push('OIDC_CLIENT_SECRET'); + + if (missingFields.length > 0) { + console.error( + `Cannot load OIDC provider "${provider.name}": missing required fields: ${missingFields.join(', ')}` + ); return []; } + console.log(`Loaded OIDC provider: ${provider.name}`); providers.push(provider); } + if (providers.length === 0) { + console.warn( + 'OIDC is enabled but no valid providers are configured. Please check your environment variables.' + ); + } + return providers; } diff --git a/backend/tests/unit/modules/oidc/providerConfig.test.js b/backend/tests/unit/modules/oidc/providerConfig.test.js index a821160..af01a60 100644 --- a/backend/tests/unit/modules/oidc/providerConfig.test.js +++ b/backend/tests/unit/modules/oidc/providerConfig.test.js @@ -120,6 +120,10 @@ describe('OIDC Provider Configuration', () => { }); it('should return empty array if configuration is incomplete', () => { + const consoleErrorSpy = jest + .spyOn(console, 'error') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_NAME = 'Google'; @@ -127,6 +131,11 @@ describe('OIDC Provider Configuration', () => { const providers = providerConfig.getAllProviders(); expect(providers).toEqual([]); + expect(consoleErrorSpy).toHaveBeenCalledWith( + expect.stringContaining('Cannot load OIDC provider') + ); + + consoleErrorSpy.mockRestore(); }); }); @@ -155,6 +164,13 @@ describe('OIDC Provider Configuration', () => { }); it('should skip numbered providers with incomplete config', () => { + const consoleWarnSpy = jest + .spyOn(console, 'warn') + .mockImplementation(); + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_1_NAME = 'Google'; @@ -171,6 +187,15 @@ describe('OIDC Provider Configuration', () => { expect(providers).toHaveLength(1); expect(providers[0].slug).toBe('okta'); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining('Skipping OIDC provider 1') + ); + expect(consoleLogSpy).toHaveBeenCalledWith( + expect.stringContaining('Loaded OIDC provider 2: Okta') + ); + + consoleWarnSpy.mockRestore(); + consoleLogSpy.mockRestore(); }); it('should handle different settings per provider', () => { @@ -229,6 +254,10 @@ describe('OIDC Provider Configuration', () => { describe('isOidcEnabled', () => { it('should return true when OIDC is enabled with valid provider', () => { + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; process.env.OIDC_PROVIDER_NAME = 'Google'; process.env.OIDC_PROVIDER_SLUG = 'google'; @@ -238,18 +267,37 @@ describe('OIDC Provider Configuration', () => { providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(true); + + consoleLogSpy.mockRestore(); }); it('should return false when OIDC_ENABLED is false', () => { + const consoleLogSpy = jest + .spyOn(console, 'log') + .mockImplementation(); + process.env.OIDC_ENABLED = 'false'; providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(false); + + consoleLogSpy.mockRestore(); }); it('should return false when no providers configured', () => { + const consoleWarnSpy = jest + .spyOn(console, 'warn') + .mockImplementation(); + process.env.OIDC_ENABLED = 'true'; providerConfig.reloadProviders(); expect(providerConfig.isOidcEnabled()).toBe(false); + expect(consoleWarnSpy).toHaveBeenCalledWith( + expect.stringContaining( + 'OIDC is enabled but no valid providers are configured' + ) + ); + + consoleWarnSpy.mockRestore(); }); }); diff --git a/frontend/components/Shared/ProjectDropdown.tsx b/frontend/components/Shared/ProjectDropdown.tsx index 6835fb0..f66cb6f 100644 --- a/frontend/components/Shared/ProjectDropdown.tsx +++ b/frontend/components/Shared/ProjectDropdown.tsx @@ -212,6 +212,7 @@ const ProjectDropdown: React.FC = ({ disabled={disabled} className="w-full bg-transparent border-none outline-none text-sm text-gray-900 dark:text-gray-100 disabled:cursor-not-allowed pr-8" autoComplete="off" + autoFocus={dropdownOpen} />