Compare commits

...

1 Commits

Author SHA1 Message Date
github-actions[bot]
f34acd2927 [AI] sync-server: support non-RS256 OpenID ID tokens (#6524)
Auto-detect the ID-token signing algorithm from the IdP's discovery
metadata, with an optional ACTUAL_OPENID_ID_TOKEN_SIGNED_RESPONSE_ALG
override. RS256 is still preferred when advertised, so existing installs
are unaffected; IdPs that sign with ECDSA (ES256/ES384/ES512) or other
RSA variants now work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 23:13:45 +01:00
5 changed files with 105 additions and 0 deletions

View File

@@ -64,6 +64,19 @@ export async function bootstrapOpenId(configParameter) {
return {};
}
export function pickIdTokenSignedResponseAlg(issuer, configParameter) {
if (configParameter.id_token_signed_response_alg) {
return configParameter.id_token_signed_response_alg;
}
const supported = issuer?.metadata?.id_token_signing_alg_values_supported;
if (Array.isArray(supported) && supported.length > 0) {
return supported.includes('RS256') ? 'RS256' : supported[0];
}
return 'RS256';
}
async function setupOpenIdClient(configParameter) {
const issuer =
typeof configParameter.issuer === 'string'
@@ -73,6 +86,12 @@ async function setupOpenIdClient(configParameter) {
authorization_endpoint: configParameter.issuer.authorization_endpoint,
token_endpoint: configParameter.issuer.token_endpoint,
userinfo_endpoint: configParameter.issuer.userinfo_endpoint,
...(Array.isArray(
configParameter.issuer.id_token_signing_alg_values_supported,
) && {
id_token_signing_alg_values_supported:
configParameter.issuer.id_token_signing_alg_values_supported,
}),
});
const client = new issuer.Client({
@@ -83,6 +102,10 @@ async function setupOpenIdClient(configParameter) {
configParameter.server_hostname,
).toString(),
validate_id_token: true,
id_token_signed_response_alg: pickIdTokenSignedResponseAlg(
issuer,
configParameter,
),
});
return client;

View File

@@ -0,0 +1,53 @@
import { describe, expect, it } from 'vitest';
import { pickIdTokenSignedResponseAlg } from './openid';
describe('pickIdTokenSignedResponseAlg', () => {
it('returns the explicit override when provided, even if discovery suggests otherwise', () => {
const issuer = {
metadata: { id_token_signing_alg_values_supported: ['RS256', 'ES384'] },
};
expect(
pickIdTokenSignedResponseAlg(issuer, {
id_token_signed_response_alg: 'ES512',
}),
).toBe('ES512');
});
it('prefers RS256 when it is among the advertised algorithms (backwards-compat)', () => {
const issuer = {
metadata: { id_token_signing_alg_values_supported: ['ES384', 'RS256'] },
};
expect(pickIdTokenSignedResponseAlg(issuer, {})).toBe('RS256');
});
it('falls back to the first advertised algorithm when RS256 is not offered', () => {
const issuer = {
metadata: { id_token_signing_alg_values_supported: ['ES384', 'ES256'] },
};
expect(pickIdTokenSignedResponseAlg(issuer, {})).toBe('ES384');
});
it('uses a lone ECDSA algorithm (the bug-report case)', () => {
const issuer = {
metadata: { id_token_signing_alg_values_supported: ['ES384'] },
};
expect(pickIdTokenSignedResponseAlg(issuer, {})).toBe('ES384');
});
it('defaults to RS256 when the issuer exposes no signing metadata', () => {
expect(pickIdTokenSignedResponseAlg({ metadata: {} }, {})).toBe('RS256');
expect(pickIdTokenSignedResponseAlg(undefined, {})).toBe('RS256');
});
it('treats an empty override as "auto-detect"', () => {
const issuer = {
metadata: { id_token_signing_alg_values_supported: ['ES384'] },
};
expect(
pickIdTokenSignedResponseAlg(issuer, {
id_token_signed_response_alg: '',
}),
).toBe('ES384');
});
});

View File

@@ -255,6 +255,12 @@ const configSchema = convict({
default: 'openid',
env: 'ACTUAL_OPENID_AUTH_METHOD',
},
id_token_signed_response_alg: {
doc: 'Algorithm used by the OpenID provider to sign ID tokens (e.g. RS256, ES256, ES384). Auto-detected from discovery metadata when empty.',
format: String,
default: '',
env: 'ACTUAL_OPENID_ID_TOKEN_SIGNED_RESPONSE_ALG',
},
},
token_expiration: {

View File

@@ -138,4 +138,21 @@ describe('config schema', () => {
authorizationEndpoint,
);
});
it('parses the ID token signing algorithm override from the environment', () => {
process.env.TEST_OPENID_ID_TOKEN_SIGNED_RESPONSE_ALG = 'ES384';
const testSchema = convict({
openId: {
id_token_signed_response_alg: {
doc: 'Algorithm used by the OpenID provider to sign ID tokens.',
format: String,
default: '',
env: 'TEST_OPENID_ID_TOKEN_SIGNED_RESPONSE_ALG',
},
},
});
expect(() => testSchema.validate()).not.toThrow();
expect(testSchema.get('openId.id_token_signed_response_alg')).toBe('ES384');
});
});

View File

@@ -0,0 +1,6 @@
---
category: Bugfixes
authors: [MatissJanis]
---
Sync server: accept non-RS256 OpenID ID tokens. The ID-token signing algorithm is now auto-detected from the provider's discovery metadata (with an optional `ACTUAL_OPENID_ID_TOKEN_SIGNED_RESPONSE_ALG` override), fixing login against IdPs that sign with ECDSA (ES256/ES384/ES512) or other RSA variants.