Azure / static-web-apps

Azure Static Web Apps. For bugs and feature requests, please create an issue in this repo. For community discussions, latest updates, kindly refer to the Discussions Tab. To know what's new in Static Web Apps, visit https://aka.ms/swa/ThisMonth
https://aka.ms/swa
MIT License
326 stars 56 forks source link

Using MSAL http interceptor with Azure functions proxies.json still tampers with the auth header #335

Open agiletea opened 3 years ago

agiletea commented 3 years ago

I believe my scenario is affected by the behaviour stipulated in issue 34. However, I'm using an angular 11 frontend with MSAL 2.0 beta 2 and I only have a single azure function to allow a proxies.json to allow a straight pass through to an external web api. The webapi is a dotnet 5.0 api set up to use Microsoft Identity with JWT bearer authentication and all works fine when a direct api call is made. Deploying to an Azure Static Web app, I can confirm that non authorised api calls work so I know my functions proxies.json route is working as expected but when I try to hit an authorised protected api endpoint, I end up with the following:

Bearer was not authenticated. Failure message: IDX10503: Signature validation failed

On inspection of the logging, I can see that the token is landing at the api with the issuer and audience changed to that of the azure function so it seems as though even though the proxies route is kicking in, my authorization header is still being overwritten.

What's not clear is how I can stop or manage this behaviour. Even if I create a custom msal http interceptor to write a custom auth header, I'm not clear on how I can override the Microsoft Identity Web Api options to use this instead of the standard authentication bearer header. Any pointers or comments would be appreciated as I'm familiar with using Angular wrapped in an nginx container and a separate protected dotnet api through docker compose multi containers but to be able to target a simple static web app would be ideal if I can get protected external api calls cracked!

To confirm setup:

Angular v 11 MSAL 2.0.0-beta.2 MSAL-Browser: 2.13.0

Dotnet 5 webapi with Microsoft.Identity.Web 1.7.0

AAD app registration using Authorization Code flow with PKCE (i.e. no id or access token) for use with MSAL 2.0

Notable excerpts of code:

Dummy Azure Function:

    public static class DummyFunction
    {
        [FunctionName("DummyFunction")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            log.LogInformation("C# HTTP trigger function processed a request.");

          // doesn't do anything in here

            return new OkObjectResult(responseMessage);
        }
    }

local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",    
    "AZURE_FUNCTION_PROXY_DISABLE_LOCAL_CALL": true,
    "APIHOST_CRM": "https://<my-api>.azurewebsites.net"
  }
}

proxies.json

{
    "$schema": "http://json.schemastore.org/proxies",
    "proxies": {
        "backend": {
            "matchCondition": {
                "route": "/api/{*restOfPath}"
            },
            "backendUri": "%APIHOST_CRM%/api/{restOfPath}",
            "debug": true
        }
    }
}

Auth set up in webapi:

            // Setting configuration for protected web api
            services
                .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApi(Configuration);
anthonychu commented 3 years ago

If you send a custom auth header (e.g., x-custom-authorization) to the Static Web Apps API, you can try to use it to set the Authorization header in the proxy:

{
    "$schema": "http://json.schemastore.org/proxies",
    "proxies": {
        "backend": {
            "matchCondition": {
                "route": "/api/{*restOfPath}"
            },
            "requestOverrides": {
                "backend.request.headers.Authorization": "{request.headers.x-custom-authorization}"
            },
            "backendUri": "%APIHOST_CRM%/api/{restOfPath}",
            "debug": true
        }
    }
}

You should also be able to achieve the same thing in your ASP.NET app by adding a middleware to rename the header before it hits the auth middleware.

We're working on fixing this so that you can use the Authorization header, but there's no ETA at this time.

agiletea commented 3 years ago

@anthonychu, firstly, thank you so much for such a speedy and helpful response. I've ended up belts and braces and implementing both approaches to understand more. I created a custom msal interceptor to add in the custom auth header so I'm wondering if that's the approach you would expect or are there options within the MSAL library to allow me to tap into the headers to do this without replacing the whole MsalInterceptor?

For others that hit this issue:

I took the the msal interceptor code from github and simply amended the header code on line 68:

const headers = req.headers
                        .set("Authorization", `Bearer ${result.accessToken}`)
                        .set("x-custom-authorization", `Bearer ${result.accessToken}`);

I then changed my http interceptor provider declaration in my app.module code:

{
      provide: HTTP_INTERCEPTORS,
      useClass: CustomMsalInterceptor,
      multi: true
    }

(where CustomMsalInterceptor is the name of my class)

jazzolina commented 2 years ago

@agiletea thank you for your comments here -- they were extremely helpful.