FHIR Server OAUTH2 with Microsoft Entra Intersystems Health 2025.3
I am working on setting up OAUTH with FHIR and Microsoft Entra. I have configured the Client and configured it in the FHIR Server successfully. However, I get 401 when authenticating with a token. Looking at ^ISCLOG I see:
^ISCLOG("Data",9,0)="accessToken=<jwt token>, scope=, aud="
^ISCLOG("Data",10)=$lb(3,"OAuth2","[OAuth2.ServerDefinition:ValidateJWT]","171430","%SYS","2026-04-28 17:41:25.305549397","OAuth2.ServerDefinition.1","","zwKKakZZZx2")
^ISCLOG("Data",10,0)="JWT valid? sc=1"
^ISCLOG("Data",11)=$lb(3,"HSFHIRServer","[HS.FHIRServer.RestHandler:OnPreDispatch]CSP Request","171430","HEALTHSHARE","2026-04-28 17:41:25.30613001","HS.FHIRServer.RestHandler.1","","zwKKakZZZx2")
^ISCLOG("Data",11,0)="Content-Type: , Secure: 1, $Username: UnknownUser"
Any Suggestions on what the issue might be?
Comments
Update: I enabled OAuth globally and now I can get a user, create a user and assign scopes. But I still get 401.
What I've found a written about here may be relevant, but I'm not sure how to address that issue in your specific use case.
I have identified my issue, and implemented a workaround at least for my proof of concept. What I found in the FHIR Logs is the audience (aud) is not matching from the FHIR Oauth class. It appears the OAuthClass is blocking based on the audience as Entra is sending the clientID and the FHIR OAuth Class is expecting something different (my guess is endpoint?). For now, I have implemented a custom interactionStrategy and a custom OAuth2Token Handler Class to replace the standard one so this audience block does not happen. I am pending Support on this issue otherwise.
^FSLOG(24110)="ValidateToken^HS.FHIRServer.Util.OAuth2Token^170903|Msg|ValidateJWT() token aud=<Entra Client ID> |04/29/2026 12:27:34.436871PM"
^FSLOG(24111)="ValidateToken^HS.FHIRServer.Util.OAuth2Token^170903|Msg|Token aud failed validation|04/29/2026 12:27:34.436896PM"
yes, I was correct,. it is expecting the baseUrl
Method ValidateAudience() As %Boolean [ Private ]
{
Set JWTAudience = ..%TokenObject.aud
If JWTAudience'="" {
Set matched = 0
Set currentAud = $$formatAud(..%BaseURL)
If '$IsObject(JWTAudience) {
// Compare single audience from JWT.
If $$formatAud(JWTAudience)=currentAud {
Set matched = 1
}
} Else {
// Compare multiple audience from JWT. In this case it is a %DynamicArray.
For i = 0:1:JWTAudience.%Size()-1 {
If $$formatAud(JWTAudience.%Get(i))=currentAud {
Set matched = 1
Quit
}
}
}See this latest post re a new feature coming in 2026.2 that addresses your exact issue.
If you want you can give it a try with the Developer Preview version (note this is not supported for production use of course, just for your own internal testing; and share any feedback you might have, that's why it's out there).