Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion api/strategies/openIdJwtStrategy.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ const {
const { updateUser, findUser } = require('~/models');

const getOpenIdJwtAudience = () => {
const audiences = [process.env.OPENID_CLIENT_ID, process.env.OPENID_AUDIENCE].filter(Boolean);
const parsedAudience = (process.env.OPENID_AUDIENCE ?? '')
.split(',')
.map((value) => value.trim())
.filter(Boolean);
const audiences = [process.env.OPENID_CLIENT_ID, ...parsedAudience].filter(Boolean);
const uniqueAudiences = [...new Set(audiences)];

return uniqueAudiences.length > 1 ? uniqueAudiences : uniqueAudiences[0];
Expand Down
46 changes: 46 additions & 0 deletions api/strategies/openIdJwtStrategy.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,52 @@ describe('openIdJwtStrategy – token validation', () => {
});
});

it('uses a single OPENID_AUDIENCE value when no client ID is configured', () => {
withEnv({ OPENID_CLIENT_ID: undefined, OPENID_AUDIENCE: 'librechat' }, () => {
openIdJwtLogin(mockOpenIdConfig);
});

expect(capturedStrategyOptions.audience).toBe('librechat');
});

it('splits comma-separated OPENID_AUDIENCE values into multiple accepted audiences', () => {
withEnv({ OPENID_CLIENT_ID: undefined, OPENID_AUDIENCE: 'librechat,control-plane-web' }, () => {
openIdJwtLogin(mockOpenIdConfig);
});

expect(capturedStrategyOptions.audience).toEqual(['librechat', 'control-plane-web']);
});

it('trims whitespace around comma-separated OPENID_AUDIENCE values', () => {
withEnv(
{ OPENID_CLIENT_ID: undefined, OPENID_AUDIENCE: ' librechat , control-plane-web ' },
() => {
openIdJwtLogin(mockOpenIdConfig);
},
);

expect(capturedStrategyOptions.audience).toEqual(['librechat', 'control-plane-web']);
});

it('falls back to OPENID_CLIENT_ID when OPENID_AUDIENCE is empty', () => {
withEnv({ OPENID_CLIENT_ID: 'client-id-only', OPENID_AUDIENCE: '' }, () => {
openIdJwtLogin(mockOpenIdConfig);
});

expect(capturedStrategyOptions.audience).toBe('client-id-only');
});

it('combines OPENID_CLIENT_ID with comma-separated OPENID_AUDIENCE values and deduplicates', () => {
withEnv(
{ OPENID_CLIENT_ID: 'librechat', OPENID_AUDIENCE: 'librechat,control-plane-web' },
() => {
openIdJwtLogin(mockOpenIdConfig);
},
);

expect(capturedStrategyOptions.audience).toEqual(['librechat', 'control-plane-web']);
});

it('rejects OpenID JWTs whose issuer does not match the configured issuer', async () => {
findOpenIDUser.mockResolvedValue({ user: null, error: null, migration: false });
openIdJwtLogin(mockOpenIdConfig);
Expand Down
Loading