Azure / data-api-builder

Data API builder provides modern REST and GraphQL endpoints to your Azure Databases and on-prem stores.
https://aka.ms/dab/docs
MIT License
814 stars 147 forks source link

[Bug]: Static Web Apps + Data API Builder JWT Authentication #2063

Open jonatansthlmstratlab opened 4 months ago

jonatansthlmstratlab commented 4 months ago

What happened?

I am encountering an issue when I'm deploying my DAB through SWA to Azure. Specifically it is my GET-request that is not authorized. I have setup my authentication following this guide: https://learn.microsoft.com/en-us/azure/data-api-builder/authentication-azure-ad

Locally by using the SWA CLI emulator I can authenticate my request by using this authentication in my dab.config.json file:

"authentication": {
        "provider": "AzureAd",
        "jwt": {
          "audience": <APP_ID>,
          "issuer": "https://login.microsoftonline.com/<TENANT_ID>v2.0"
        }
      }

This is however not working in my preview environement when I deploy it to SWA.

Following the accepted reply in this thread (in which I also replied in hopes of some guidance) https://github.com/Azure/data-api-builder/discussions/1720?sort=top, I change the "authentication" to Static Web Apps and also add the "auth" in the staticwebapp.config.json it is not able to authenticate my request either locally or in my preview environment.

"authentication": {
        "provider": "StaticWebApps"
      }

If I set the role to anonymous it is working both in my SWA CLI emulator and in my SWA preview environment.

This is how my staticwebapp.config.json looks like where I have set the AZURE_CLIENT_ID and AZURE_CLIENT_SECRET variables in the Azure portal:

{
    "routes": [
        {
            "route": "/authenticated/*",
            "allowedRoles": [
                "authenticated",
                "Sample.Role",
                "SampleRole"
            ]
        },
        {
            "route": "/data-api/*",
            "allowedRoles": [
                "authenticated",
                "Sample.Role",
                "SampleRole"
            ]
        },
        {
            "route": "/rest/*",
            "allowedRoles": [
                "authenticated",
                "Sample.Role",
                "SampleRole"
            ]
        }
    ],
    "auth": {
        "identityProviders": {
            "azureActiveDirectory": {
                "registration": {
                    "openIdIssuer": "https://login.microsoftonline.com/<Tenant_id>/v2.0",
                    "clientIdSettingName": "AZURE_CLIENT_ID",
                    "clientSecretSettingName": "AZURE_CLIENT_SECRET"
                }
            }
        }
    }
}

And this is my dab.config.json:

{
  "$schema": "https://github.com/Azure/data-api-builder/releases/latest/download/dab.draft.schema.json",
  "data-source": {
    "database-type": "mssql",
    "connection-string": "@env('connection-string')",
    "options": {
      "set-session-context": false
    }
  },
  "runtime": {
    "rest": {
      "enabled": true,
      "path": "/rest",
      "request-body-strict": true
    },
    "graphql": {
      "enabled": false,
      "path": "/graphql",
      "allow-introspection": true
    },
    "host": {
      "cors": {
        "origins": [
          "https://yellow-sea-0cdd5f803-8.westeurope.4.azurestaticapps.net",
          "http://localhost:5000"],
        "allow-credentials": false
      },
      "authentication": {
        "provider": "StaticWebApps"
      },
      "mode": "production"
    }
  },
  "entities": {
    "Companies": {
      "source": {
        "object": "dw.Companies",
        "key-fields": ["Company_ID"],
        "type": "table"
      },
      "graphql": false,
      "permissions": [
        {
          "actions": ["read"],
          "role": "authenticated"
        },
        {
          "actions": ["read"],
          "role": "Sample.Role"
        },
        {
          "actions": ["read"],
          "role": "SampleRole"
        }
      ]
    }
  }
}

All the different roles in both the dab.config.json and swa.config.json is for testing purposes.

Version

1.1.6

What database are you using?

Azure SQL

What hosting model are you using?

Static Web Apps (SWA)

Which API approach are you accessing DAB through?

REST

Relevant log output

No response

Code of Conduct

seantleonard commented 4 months ago

@jonatansthlmstratlab thanks for reaching out. Is 1.1.6 referring to the version of a different tool?

With regards to your configuration, the first thing that stands out is the SWA route config for /rest. If this is intended to be for the REST endpoint exposed by SWA Database Connections, then that route should probably be /data-api/rest/* but I'd imagine that should be covered by your wildcard entry /data-api/*

Just to confirm since I didn't see you explicitly mention this:

  1. Are you authenticating with your SWA endpoint by using https://<swa_instance>.azurestaticapps.net/.auth/login/aad?
  2. Have you updated your app registration in Entra ID to permit SWA's callback URL to be used?
    • I took a look at this doc and looks like you'll also need to add the url https://<YOUR_SITE>/.auth/login/<PROVIDER_NAME_IN_CONFIG>/callback to your App Registrations list of valid callback URLs. SWA doc ref
jonatansthlmstratlab commented 4 months ago

@seantleonard thanks for your quick response.

Sorry, the version should be 0.10.23, 1.1.6 refers to my swa db version.

So for context, I'm trying to use SWA + DAB purely for the no-code approach of having rest-endpoints to access tables in our Azure SQL database. I am not deploying a website along with this, so the SWA is only intended to be a simple placeholder. The intent is to get an access token from my app registration and then do a simple pull request of the data that is exposed in the dab.config.json and then push that data into another service.

So I'm generating the token through the AZ CLI az account get-access-token --scope api://<APP_ID>/Endpoint.Access (later, the intent is to do this through python) and then providing that access token in my get request along with the header 'X-MS-API-ROLE': 'Sample.Role'.

As to your questions:

  1. I am authenticating through api://<APP_ID>/Endpoint.Access
  2. I have not updated permission for the callback URL. Would that be this Redirect URI?Skärmavbild 2024-02-29 kl  09 57 37
seantleonard commented 4 months ago

I'm not certain using AZ CLI will actually authenticate requests to a SWA app. Can you try authenticating in browser at https://<swa_instance>.azurestaticapps.net/.auth/login/aad and then making a subsequent request to your /data-api SWA endpoint to verify?

For number 2, yes, redirect URL is where you'd update callback permissions.

By configuring DAB to use the Authentication Provider StaticWebApps, you're instructing DAB to depend on the authentication mechanisms provided by SWA (this is alternatively referred to as "EasyAuth"). SWA will handle all the token flow logic and passes the authenticated requestor's metadata/claims to DAB. DAB in this case is not directly processing JWT tokens issued by any identity provider. More info on how this works can be found: https://learn.microsoft.com/azure/app-service/overview-authentication-authorization#feature-architecture . The article is for AppService, but the concepts are applicable to SWA.

jonatansthlmstratlab commented 4 months ago

So when I try to authenticate through https://<swa_instance>.azurestaticapps.net/.auth/login/aad I'm getting this error message: AADSTS700054: response_type 'id_token' is not enabled for the application.

All right, I'll try adding the redirct URL as well

seantleonard commented 4 months ago

So when I try to authenticate through https://<swa_instance>.azurestaticapps.net/.auth/login/aad I'm getting this error message: AADSTS700054: response_type 'id_token' is not enabled for the application.

All right, I'll try adding the redirct URL as well

These steps will guide you through configuring your App Registration for use with SWA and points out which settings need to be updated to not get the id_token error you highlighted: https://learn.microsoft.com/azure/app-service/configure-authentication-provider-aad?tabs=workforce-tenant#-option-2-use-an-existing-registration-created-separately

Under the "Authentication" blade of your AppRegistration:

Under Implicit grant and hybrid flows, enable ID tokens to allow OpenID Connect user sign-ins. Select Save.

jonatansthlmstratlab commented 4 months ago

I managed to resolve the id-token error and can log in to the website as well as see the data in my table in the browser.

But how would I manage to do this through python? Even though I manage to authenticate in a Post-request and creating a session I am still getting the 401 response in my subsequent get-request

seantleonard commented 4 months ago

I don't have an immediate answer for you since I'm not certain whether Static Web Apps supports programmatic-only authenticated access to the /data-api endpoint, since interactions with SWA are expected to be through the browser where the session cookie is stored.

This article from @aaronpowell outlines a way to accomplish this but the mechanism described in the article may not be possible anymore/ and is not officially supported: https://www.aaron-powell.com/posts/2021-07-02-calling-static-web-apps-authenticated-endpoints/

jonatansthlmstratlab commented 4 months ago

Hmm okay. That is unfortunate. Would it be possible of doing this through hosting DAB in a container instead?