Skip to content

feat(#10650): support configuring max_age parameter for OIDC login#10801

Open
saurabh12nxf wants to merge 3 commits intomedic:masterfrom
saurabh12nxf:fix-10650-oidc-max-age
Open

feat(#10650): support configuring max_age parameter for OIDC login#10801
saurabh12nxf wants to merge 3 commits intomedic:masterfrom
saurabh12nxf:fix-10650-oidc-max-age

Conversation

@saurabh12nxf
Copy link
Copy Markdown

Description

This PR implements support for the OIDC max_age parameter to address issues with shared device scenarios where users may be automatically logged in as the previous user without being prompted for authentication.

Changes Made

  • Modified api/src/services/sso-login.js:

    • Updated oidcServerSConfig() to return both idServerConfig and oidcProvider settings
    • Added max_age parameter to authorization URL when configured in oidc_provider settings
    • Added maxAge validation to token verification to check the auth_time claim
  • Added comprehensive test coverage in api/tests/mocha/services/sso-login.spec.js:

    • Test for max_age parameter in authorization URL (normal case)
    • Test for max_age: 0 edge case in authorization URL
    • Test for maxAge validation in token verification (normal case)
    • Test for maxAge: 0 edge case in token verification

How It Works

When max_age is configured in the oidc_provider settings, the OIDC authorization flow will:

  1. Include the max_age parameter in the authorization URL sent to the auth provider
  2. The auth provider will check when the user last authenticated
  3. If the time elapsed exceeds max_age seconds, the user will be prompted to re-authenticate
  4. The returned ID token includes an auth_time claim which is validated against the configured maxAge

Configuration

Add max_age (in seconds) to the oidc_provider configuration in the instance settings document:

{
  "oidc_provider": {
    "discovery_url": "https://auth-provider.com/.well-known/openid-configuration",
    "client_id": "your-client-id",
    "max_age": 60
  }
}

Setting max_age: 0 is equivalent to prompt=login per the OIDC specification, forcing immediate re-authentication.

Fixes #10650

Code review checklist

  • Readable: Concise, well named, follows the style guide
  • Documented: Configuration and user documentation on cht-docs
  • Tested: Unit and/or e2e where appropriate
  • Backwards compatible: Works with existing data and configuration or includes a migration. Any breaking changes documented in the release notes.
  • AI disclosure: AI assistance (Claude/Kiro) was used for debugging and refining the implementation.

License

The software is provided under AGPL-3.0. Contributions to this project are accepted under the same license.

@saurabh12nxf saurabh12nxf changed the title feat(#10650): Support configuring max_age parameter for OIDC login feat(#10650): support configuring max_age parameter for OIDC login Apr 2, 2026
@jkuester jkuester self-requested a review April 2, 2026 16:25
Copy link
Copy Markdown
Contributor

@jkuester jkuester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good @saurabh12nxf!

Can you also update the integration tests to test this logic. Basically we want to confirm that when max_age is configured, it is passed as a param. Then, when the server returns the auth_time, we should confirm (if possible) that the value of the claim is validated (and very old auth times are rejected). We don't need to get too crazy checking every edge case, but it would be nice to hit the happy path for max_age and then have one test that does fail the auth_time validation. Probably will require changes to our mock oidc provider.

Comment thread api/src/services/sso-login.js Outdated
Comment on lines +91 to +100
if (oidcProvider.max_age !== undefined && oidcProvider.max_age !== null) {
params.max_age = oidcProvider.max_age;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but I guess we can just directly pass the max_age value here.

Suggested change
const { idServerConfig, oidcProvider } = await oidcServerSConfig();
const params = {
redirect_uri: redirectUrl,
scope: 'openid email',
max_age: oidcProvider.max_age
};

Comment thread api/src/services/sso-login.js Outdated
Comment on lines +123 to +126
const checks = { idTokenExpected: true };
if (oidcProvider.max_age !== undefined && oidcProvider.max_age !== null) {
checks.maxAge = oidcProvider.max_age;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const checks = { idTokenExpected: true };
if (oidcProvider.max_age !== undefined && oidcProvider.max_age !== null) {
checks.maxAge = oidcProvider.max_age;
}
const checks = {
idTokenExpected: true,
maxAge: oidcProvider.max_age
};

Should be able to just set this directly.

Looking at the oauth4webapi validation logic (used under the hood by openid-client) when the auth_time is validated, it is with a default tolerance of 30s which should be fine for our purposes.

We do need to bump the version of the openid-client dependency in the root package.json to the latest release (6.8.2) so that we get this fix for max_age: 0... 😅

@saurabh12nxf
Copy link
Copy Markdown
Author

Thanks for the review @jkuester! I've made the requested changes:

  • Simplified the max_age handling by removing unnecessary undefined/null checks
  • Updated openid-client from 6.4.2 to 6.8.2 (includes fix for max_age: 0)
  • Updated unit tests accordingly
  • All unit tests passing (26/26)

For the integration tests, I'd appreciate some guidance on how to set up the mock OIDC provider to test the max_age flow and auth_time validation. Should I look at extending the existing mock provider in tests/integration/api/controllers/login.spec.js?

@jkuester
Copy link
Copy Markdown
Contributor

For the integration tests, I'd appreciate some guidance on how to set up the mock OIDC provider to test the max_age flow and auth_time validation. Should I look at extending the existing mock provider in tests/integration/api/controllers/login.spec.js?

The login.spec.js integration tests run the mock OIDC Provider.

That mock provider is effectively just an Express server that "handles" OIDC requests as if it was a real OIDC provider. We will need to make some kind of updates to this service (I have not had a chance to look too closely yet). Basically it I think we need some logic to have it return the auth_time claim when the max_age parameter is submitted and then we will need some way for the tests to customize the value the server returns for the auth_time claim... (I think...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support configuring max_age parameter for OIDC login

2 participants