AzureAD / microsoft-identity-web

Helps creating protected web apps and web APIs with Microsoft identity platform and Azure AD B2C
MIT License
664 stars 204 forks source link

Support protecting web APIs with app services authentication #1806

Open andygjp opened 2 years ago

andygjp commented 2 years ago

Microsoft.Identity.Web Library

Microsoft.Identity.Web

Microsoft.Identity.Web version

1.25.1

Web app

Not Applicable

Web API

Not Applicable

Token cache serialization

Not Applicable

Description

The WEBSITE_AUTH_DEFAULT_PROVIDER environment variable is not set when an Azure WebApp, using Easy Auth, configures unauthenticated requests to return 401.

This means AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled returns false and AppServicesAuthenticationHandler does not work as expected, ie AuthenticationMiddleware does not set HttpContext.User with the authenticated principal

Reproduction steps

  1. Deploy this app, https://github.com/andygjp/MissingAadProvider, to a Web App. (The included pipeline script creates a Linux Web App, but the result is the same if you use a Windows Web App.)
  2. At the Authentication blade, add Microsoft Identity Provider and accept the defaults. (I used a single tenant.)
  3. Browse https://win-easy-auth-lead-651.azurewebsites.net/debug/auth-me - it should display your identity.
  4. Change the Authentication settings, found in the Authentication blade, from returning 302 for unauthenticated requests to 401.
  5. Browse https://win-easy-auth-lead-651.azurewebsites.net/debug/auth-me - it should display "not authenticated".

Error message

No response

Id Web logs

No response

Relevant code snippets

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services.AddAuthentication(defaultScheme: AppServicesAuthenticationDefaults.AuthenticationScheme)
    .AddAppServicesAuthentication();

var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

// DebugController.cs
[ApiController]
[Route("[controller]")]
public sealed class DebugController : ControllerBase
{
    [HttpGet("auth-me")]
    public object GetAuth()
    {
        return new
        {
            Name = User.Identity is { IsAuthenticated: true }
                ? User.Identity.Name
                : "not authenticated",
            Email = User.FindFirstValue(ClaimTypes.Email),
            Iss = User.FindFirstValue("iss"),
            Ver = User.FindFirstValue("ver"),
        };
    }
}

Regression

No response

Expected behavior

The result at step 5, of the reproduction steps, should be the same as step 3, ie it should return you identity.

jmprieur commented 2 years ago

Thanks @andygjp for raising this. I was wondering why you'd change this setting? Could you please explain your scenario?

Also if EasyAuth does not set the environment variable, the app is considered not hosted in App Service authentication. I'm not sure what we can do in this case. Would you have an idea?

andygjp commented 2 years ago

Hi @jmprieur,

I changed the setting according to the recommendations in the Azure Portal: image

My actual use case is an API. So I thought it might be "better" to return a 401. (I quite like using the default setting because it redirects to the login page and it saves me a step.) Does returning 302 instead of a 401 cause issues for my frontend?

I don't understand why the environment variable is missing - I think it should be there regardless of what response I send back. Therefore, I think the thing that sets that environment variable should be fixed such that it sets the environment variable whether Easy Auth responds with 302, 401 or 403. If that is not possible then I think AppServicesAuthenticationInformation.IsAppServicesAadAuthenticationEnabled should be changed to look for other indications that AAD is being used as the identity provider.

(I'm working on a workaround. If the environment provider is missing it will check the value of the X-MS-CLIENT-PRINCIPAL-IDP header and will set the WEBSITE_AUTH_DEFAULT_PROVIDER environment variable if the header value is "AAD". Is this a good idea?)

jmprieur commented 2 years ago

Thanks @andygjp for the explanation. I'll ask App services authentication people why the environment variable is not set.

I would not make a decision based purely on headers (and not on environment variables), because you would have no guaranty that the headers come from App services authentication: the fact that app services authentication: 1) hosts your app 2) provides environment variables is a proof it really hosts your app (which therefore can consider the headers as legit)

jmprieur commented 2 years ago

I checked the Ms.Id.Web code and App services authentication is only supported for web apps (not web APIs) So I guess there could be an other environment variable.

Would you mind changing the title of the issue to "Support protecting web APIs with app services authentication" ? and turn it to a feature request?

andygjp commented 2 years ago

Will do.

(Just out of interest, what is the "Ms.Id.Web" code? Is that another repo?)

jmprieur commented 2 years ago

This is the same repo, just initials, Ms.Id.Web = MicroSoft.IDentity.WEB

epomatti commented 1 year ago

@jmprieur can you provide an update on this? I think I have the same scenario.

Following these steps, I'm exchanging the AAD-generated access_token by an App Service token calling "/.auth/login/aad".

Then, I call my API route using the authenticationToken response as a X-ZUMO-AUTH header. I get a 401 Unauthorized.

builder.Services.AddAuthentication(defaultScheme: AppServicesAuthenticationDefaults.AuthenticationScheme)
    .AddAppServicesAuthentication();

I can verify by the logs that the request is being accepted by App Service, but is rejected by the middleware:

2023-03-26T02:00:44.523524912Z: [INFO]  info: Microsoft.Identity.Web.AppServicesAuthenticationHandler[12]
2023-03-26T02:00:44.523573915Z: [INFO]        AuthenticationScheme: AppServicesAuthentication was challenged.

Value for WEBSITE_AUTH_DEFAULT_PROVIDER is also empty for me.