Search code examples
asp.net-coreazure-web-app-servicerazor-pagesmicrosoft-identity-platformmicrosoft-identity-web

Microsoft.Identity.Web.UI works locally but not in App Service


I've been trying to add auth to my web app following: https://github.com/Azure-Samples/active-directory-aspnetcore-webapp-openidconnect-v2/tree/master/1-WebApp-OIDC/1-1-MyOrg

If I run locally with appsettings.Development.json via dotnet run, I can log in using my organization credentials as expected. When I containerize and deploy to my Web App in Azure, I do not get logged in successfully. The url in the browser stays at /signin-oidc and goes to the Error page from the default Razor pages app.

The App Service logs have messages saying .AspNetCore.Correlation.OpenIdConnect.[key?] cookie not found

[Update] The auth flow works on my phone but not on desktop.

  1. Why would the same code work locally but not deployed?
  2. Why isn't the cookie found?
  3. Why does it work on iOS but not Windows?

Solution

  • tl;dr - Setting the same-site cookie options in the authentication settings fixed this.

    services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(options =>
        {
            Configuration.Bind("AzureAd", options);
            options.NonceCookie.SameSite = SameSiteMode.Unspecified;
            options.CorrelationCookie.SameSite = SameSiteMode.Unspecified;
        });
    

    This seems to be because the App Service enforces TLS-only and handles the TLS termination. Because of this, requests to the application are always HTTP even though the browser URL is HTTPS. This causes a number of problems, the first was that the redirect URL never matched since the Identity library uses the request scheme to form the redirect URL. I had "fixed" this by injecting a redirect URL rewrite:

    services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(options =>
        {
            Configuration.Bind("AzureAd", options);
            options.Events ??= new OpenIdConnectEvents();
            options.Events.OnRedirectToIdentityProvider += _fixRedirect;
        });
    
    ...
    
    private async Task _fixRedirect(RedirectContext context)
    {
        context.Request.Scheme = "https";
        if(!context.ProtocolMessage.RedirectUri.StartsWith("https"))
            context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http", "https");
        await Task.CompletedTask;
    }
    

    However, the correlation cookie also seems to use the same HTTP request so that when the redirect comes back, the HTTPS doesn't match. Relaxing the SameSiteMode explicitly tells the browser(?) to allow the differing schemes. This is why my phone, with a different browser with different cookie policy, worked while desktop did not. Running locally using the dev-certs allowed the schemes to match since the app was terminating the TLS rather than the App Service.

    I hooked into the CookiePolicyOptions.OnAppendCookie event to inspect the cookies while debugging to find this out.