Add OAuth client_credentials grant support for FHIR server authentication#2348
Add OAuth client_credentials grant support for FHIR server authentication#2348dionmcm wants to merge 9 commits into
Conversation
When the IG publisher runs for a long time, statically configured bearer tokens can expire. This adds support for configuring clientId, clientSecret, and tokenEndpoint so the library can manage OAuth tokens itself using the client_credentials grant. - Add clientId, clientSecret, tokenEndpoint fields to ServerDetailsPOJO - Create HTTPTokenManager for token lifecycle (fetch, cache, expiry) - Add client_credentials auth to ManagedFhirWebAccessor and ManagedWebAccessor - Add 401/403 retry with token invalidation for client_credentials servers - Add addServerAuthDetail() to ManagedWebAccess for programmatic configuration - Add validation in FhirSettings for required client_credentials fields
Implement OAuth 2.0 client_credentials flow for token management, enabling automatic fetching, caching, and refreshing of bearer tokens. Update `ManagedFhirWebAccessor`, `HTTPTokenManager`, and related classes to handle client_credentials authentication, including token invalidation and 401/403 retries. Extend `ServerDetailsPOJO` and authentication modes to support this functionality.
…non-JSON responses Improve retry logic with debug and warning logs for OAuth token refresh scenarios, covering both 401 and 403 responses. Ensure clear error propagation by validating token endpoint responses and throwing IOExceptions for invalid or non-JSON content. Adjust tests accordingly.
…uthentication method support
…retry logic for token-based requests
|
@dionmcm thank you for the contribution. The logic so far looks reasonable, but we recently merged breaking changes to ManagedWebAccess its related classes: #2341 Since you are most familiar with this change, could you adjust your PR or open a new one that uses the newer code? It looks like most of your code will still work, but that the logic in org.hl7.fhir.utilities.http.ManagedWebAccessorBase#resolveAuth should be moved to Please let me know if you have any questions or require assistance. |
dotasek
left a comment
There was a problem hiding this comment.
See PR comment regarding breaking changes to master. Otherwise, there are a few things I noticed on this quick review that should be considered.
I will re-review once the changes from master have been included.
| return new CachedToken(accessToken, expiresAtMillis); | ||
| } | ||
|
|
||
| private static String readResponseBody(HttpURLConnection conn) throws IOException { |
There was a problem hiding this comment.
This deviates from our usage of org.hl7.fhir.utilities.FileUtilities#streamToBytes, which is used here for a similar purpose:
| headers.put("Api-Key", getToken()); | ||
| } | ||
|
|
||
| private Map<String, String> newHeaders(String url) throws IOException { |
There was a problem hiding this comment.
When adjusting for the recent changes in master, keep in mind that this should definitely not be here. Auth should only be managed by IHTTPAuthenticationProvider implementations for now on.
|
In the MII in Germany, we would be happy to use this |
Purpose
The IG Publisher uses this code to authenticate to the configured FHIR terminology server. The existing code supports OAuth by providing a static token in configuration, however if the IG Publisher runs longer than the token expiry the IG Publisher process will fail. This was reported for users with a 30 minute token expiry policy they cannot change, unable to complete the IG Publisher run.
This code addresses this by adding configuration to supply client_id, client_secret, and token endpoint URL. The change uses that configuration to get a token instead of a statically configured token, monitors the token for expiry, and gets a new token when required.
Summary
client_credentialsgrant type as a new authentication mode for configured FHIR servers, with automatic token acquisition, caching, and transparent refreshfhir-settings.jsonwithauthenticationType: "client_credentials"plusclientId,clientSecret, andtokenEndpointfields, or programmatically viawithClientCredentials()resolveAuth()method inManagedWebAccessorBase, eliminating 6 duplicated auth dispatch blocks acrossManagedWebAccessorandManagedFhirWebAccessorexecuteWithTokenRetry()template method in the base classParamNamesconstants for the validation HTTP handler, replacing scattered string literalsTest plan
HTTPTokenManagerTest— 8 tests covering token fetch, caching, expiry, invalidation, and error handlingManagedWebAccessAuthTests— 14 tests covering all auth modes (basic, token, apikey, client_credentials) for both accessor types, including 401/403 retryOAuthClientCredentialsIntegrationTest— 7 end-to-end tests with MockWebServer covering the full OAuth flow, transparent token refresh on expiry, and retry on 401/403ManagedFhirWebAccessorTests— 2 tests for managed header constructionFhirSettingsTests— 7 tests including validation of required client_credentials fieldsFhirValidatorHttpServiceTests— updated forParamNamesconstant usage