Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configure Custom Identity provider #3921

Open
LodewijkSioen opened this issue Jun 14, 2024 · 17 comments
Open

Configure Custom Identity provider #3921

LodewijkSioen opened this issue Jun 14, 2024 · 17 comments

Comments

@LodewijkSioen
Copy link

I'm pretty sure I had this working at one time, but now I cannot get the custom Identity Provider to work. I have IdentityServer running in an app service. This is the token it generates:

{
  "nbf": 1718369848,
  "exp": 1718373448,
  "iss": "https://[redacted].azurewebsites.net",
  "aud": "api-m2m",
  "client_id": "3f53a72a-32ab-4e40-9ae5-08dc8c4d08cd",
  "appid": "sandbox-ls",
  "roles": "smartUser",
  "scp": "system/*.read",
  "jti": "565DA07050D5AB8058057609519D4BE5",
  "iat": 1718369848,
  "scope": "system/*.read"
}

I checked the fields a hundred times:

  • Authority Field: https://[redacted].azurewebsites.net
    • Client ID: sandbox-ls (the appid claim)
    • Audience: api-m2m (the aud claim)

And I get the following answer from the FHIR service:

HTTP/1.1 403 Forbidden

{
  "resourceType": "OperationOutcome",
  "id": "28294463e393a1ffdcfc1c203d370539",
  "meta": {
    "lastUpdated": "2024-06-14T13:11:01.8220825+00:00"
  },
  "issue": [
    {
      "severity": "error",
      "code": "forbidden",
      "diagnostics": "Authorization failed."
    }
  ]
}

Which means that my settings are correct, otherwise I would have gotten a 401.

Why am I getting Forbidden? How can I troubleshoot this?

@LodewijkSioen LodewijkSioen added the Question Issue is a question? label Jun 14, 2024
@EXPEkesheth
Copy link
Collaborator

@LodewijkSioen - Have you assigned the SMART user role ?

@LodewijkSioen
Copy link
Author

LodewijkSioen commented Jun 17, 2024

As you can see in my JWT, I've put "roles": "smartUser" in there.

Mind that I'm using my own Identity Provider and EntraID as specified in the documentation. That documentation does not talk about any roles.

@feordin
Copy link
Contributor

feordin commented Jun 17, 2024

You are correct that the token is valid, and that is why you see a 403 instead of a 401. Perhaps you have App Insights configured for your app service? It should have more details about why the 403 was returned.

@LodewijkSioen
Copy link
Author

LodewijkSioen commented Jun 17, 2024 via email

@LodewijkSioen
Copy link
Author

So I've dug deeper and I'm pretty sure it's the _authorizationService.CheckAccess method that gives me the 403. So that means that my roles claim is not correct. @EXPEkesheth can you confirm that I correctly configure the roles claim to use it on Azure. It works locally, but perhaps the name of the role is different on Azure?

@LodewijkSioen
Copy link
Author

I made a minimal demo project to show you what I've done: https://github.com/LodewijkSioen/fhir-samples/tree/main/identity-provider

@EXPEkesheth
Copy link
Collaborator

@feordin - can you please review the demo project and help ?

@feordin
Copy link
Contributor

feordin commented Jun 24, 2024

I will take a look at the sample and see if I can repro the issue.

@feordin
Copy link
Contributor

feordin commented Jun 24, 2024

I downloaded the sample (thank you for that!) and I was able to get tokens that were validated and functioning.

I did make one update to the Identity Provider to include the "roles" claim:

    public Task ValidateAsync(CustomTokenRequestValidationContext context)
    {
        if (context.Result.ValidatedRequest.RequestedScopes.Contains(Constants.Userscope))
        {
            context.Result.ValidatedRequest.ClientClaims.Add(new Claim("fhirUser", "https://example.org/Practitioner/123456798"));
        }

        context.Result.ValidatedRequest.ClientClaims.Add(new Claim("roles", "smartUser"));

With that change I get a token like this for system scope:

{
  "alg": "RS256",
  "kid": "B699F4C1E63E8F38012738C00B738BE5",
  "typ": "JWT"
}.{
  "nbf": 1719253724,
  "exp": 1719257324,
  "iss": "https://localhost:7023",
  "aud": "smart-fhir-test-audience",
  "client_id": "test",
  "azp": "smart-fhir-test-clientid",
  "roles": "smartUser",
  "scp": "system/*.read",
  "jti": "2DD4592B05BB4663EF983374144A31AD",
  "iat": 1719253724
}.[Signature]

or for user scope I get this token:

{
  "alg": "RS256",
  "kid": "B699F4C1E63E8F38012738C00B738BE5",
  "typ": "JWT"
}.{
  "nbf": 1719254168,
  "exp": 1719257768,
  "iss": "https://localhost:7023",
  "aud": "smart-fhir-test-audience",
  "client_id": "test",
  "azp": "smart-fhir-test-clientid",
  "fhirUser": "https://example.org/Practitioner/123456798",
  "roles": "smartUser",
  "scp": "user/*.read",
  "jti": "2A2A5BD7D8F83023F39AB3E580908C49",
  "iat": 1719254168
}.[Signature]

either of those tokens succeed with a 200.

To match the tokens my appsettings.json for the FHIR service has the following security configuration:

    "FhirServer": {
        "Security": {
            "Enabled": true,
            "EnableAadSmartOnFhirProxy": true,
            "Authentication": {
                "Audience": "smart-fhir-test-audience",
                "Authority": "https://localhost:7023"
            },

You can see that the Audience matches the "aud" claim in the token, and the Authority matches the "iss" claim in the token. The role matches "smartUser" which is defined in roles.jon in the FHIR service code. That will result in the proper data actions being returned by the CheckAccess method that you already mentioned.

I hope this helps!

@LodewijkSioen
Copy link
Author

Thank you for looking into this. I already had it working against the OSS version of the Fhir Server, but it's against the Azure PaaS version that I cannot get it working.

@LodewijkSioen
Copy link
Author

@feordin I dug deeper and I suspect that the Azure FHIR Service is configured to either:

  • Use a different claim to identify the role via RolesClaim
  • Use a different name for the smartUser role via roles.json

But there is no way for me to know of investigate this. Also the fact that a specific role is needed is missing from the documentation.

@EXPEkesheth
Copy link
Collaborator

@LodewijkSioen is the issue resolved or are you looking for any further inputs?

@LodewijkSioen
Copy link
Author

LodewijkSioen commented Jul 8, 2024 via email

@feordin
Copy link
Contributor

feordin commented Jul 23, 2024

@LodewijkSioen I have tried the scenario again for the Azure PaaS version. the configuration is a bit different, and it does not use the roles.json file. When using Azure Entra ID as the identity propvider we would read role infomration via Azure RBAC. For 3rd party IDPs we limit the user to only the "smart" role, and role membership is assumed. We validate the token using the appId and issuer to ensure those values match the entries in the portal. Then we check the fhirUserClaim to ensure it matches a resource in the FHIR server and determine the compartment of resources that should be made available to be read.

I published the sample identity provider code in fhir-samples repo you mentioned to an app service. Then I configured my PaaS instance with the following values:
image

I made a change to the identity provider so that it would populate the fhirUserClaim token with a valid url of a practitioner in the FHIR server:

    if (context.Result.ValidatedRequest.RequestedScopes.Contains(Constants.Userscope))
    {
        context.Result.ValidatedRequest.ClientClaims.Add(new Claim("fhirUser", "https://jaerwin-identitytest.fhir.azurehealthcareapis.com/Practitioner/123456798"));
    }

It then generated a token like this:
{
"alg": "RS256",
"kid": "E95C1005887923B229629BDB6D5A0557",
"typ": "JWT"
}.{
"nbf": 1721770730,
"exp": 1721774330,
"iss": "https://identityserver20240722190056.azurewebsites.net",
"aud": "smart-fhir-test-audience",
"client_id": "test",
"azp": "smart-fhir-test-clientid",
"fhirUser": "https://jaerwin-identitytest.fhir.azurehealthcareapis.com/Practitioner/123456798",
"roles": "smartUser",
"scp": "user/*.read",
"jti": "F2F7D6AA44F05FF070D3FF5A6B2FF818",
"iat": 1721770730
}.[Signature]

With that token I was able to successfully read resources within the practitioner compartment.

@LodewijkSioen
Copy link
Author

Ah yes, so for the user scope, you need the full url including the domain in the fhirUser claim. This is annoying since we don't want to expose the internal azure url, but I can work around that. I'de rather not leak the internal URL to a client, but at least it's hidden in inside an encoded token.

A more pressing issue is that I cannot get the system scopes to work (eg system/*.read). Are these not supported on external ID providers?

@EXPEkesheth EXPEkesheth added the Review Tag for PM/Dev Review label Jul 30, 2024
@EXPEkesheth
Copy link
Collaborator

@LodewijkSioen system scopes are not supported on external ID providers.

@LodewijkSioen
Copy link
Author

That's too bad because the system scopes are what we wanted to use in our system :(

@EXPEkesheth EXPEkesheth added Area-SMART and removed Question Issue is a question? Review Tag for PM/Dev Review labels Sep 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants