OWIN: IIS throws Access denied for authenticated u

2019-09-09 23:30发布

问题:

Background
I have web forms application that use OWIN + Azure AD for authentication. The application is hosted in IIS 10.0.10586.0. The application is hosted at root ( Default Web Site) as www.mydomain.com . I also have child .net application (not virtual directory) which contains some static pages and some .Net code. So authenticated user can access static pages as www.mydomain.com/pages/page1.html

Steps I followed:
1> Create web form application and implanted Azure AD authentication using OWIN as mentioned here or here
2> Hosted application under IIS 10 as root application ( Default Web Site)
3> Everything works fine at this point, User gets redirected to Microsoft’s login page, get authenticated there and then upon successful authentication get redirected back to mydomain.com
4> I see System.Web.HttpContext.Current.Request.IsAuthenticated is “true”
5> I have some static pages that I want only Authenticated user to have access. So I created a new application under default web site, and set physical path accordingly.
6> I also added authorization & runAllManagedModulesForAllRequests in web.config (see below)
7> When authenticated user tries to access pages www.mydomain.com/pages/page1.html he gets access denied error

(NOTE if i create child virtual directory it works. but i would like to have application instead of virtual directory)

Access Denied
Error message 401.2.: Unauthorized: Logon failed due to server configuration. Verify that you have permission to view this directory or page based on the credentials you supplied and the authentication methods enabled on the Web server. Contact the Web server's administrator for additional assistance.

Below is the snapshot of IIS & application pool ( I have also tried setting identity to LocalSystem) "pages" is the child application that authenticated user cannot access.

OWIN Startup class

   [assembly: OwinStartup(typeof(AzureWithSL.Web.Startup))]
   namespace AzureWithSL.Web
   {
     public partial class Startup
     {
      private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
      private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
      private static string tenant = ConfigurationManager.AppSettings["ida:Tenant"];
      private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
      string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);

     public void Configuration(IAppBuilder app)
     {
        ConfigureAuth(app);
     }       

     public void ConfigureAuth(IAppBuilder app)
     {
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

        app.UseCookieAuthentication(new CookieAuthenticationOptions());

        app.UseOpenIdConnectAuthentication(
            new OpenIdConnectAuthenticationOptions
            {                   
                ClientId = clientId,
                Authority = authority,
                PostLogoutRedirectUri = postLogoutRedirectUri,
                Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = context =>
                    {
                        context.HandleResponse();
                        context.Response.Redirect("/Error?message=" + context.Exception.Message);
                        return Task.FromResult(0);
                    }
                }
            });
     }
   }   
 }

Application Startup Page

public class Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        SignIn();
    }

    public void SignIn()
    {
        // Send an OpenID Connect sign-in request.
        if (!System.Web.HttpContext.Current.Request.IsAuthenticated)
        {
            HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/" }, OpenIdConnectAuthenticationDefaults.AuthenticationType);
        }
    }
}

Web.Config

<configuration>
     <appSettings>
      <add key="webpages:Version" value="3.0.0.0" />
      <add key="webpages:Enabled" value="false" />
      <add key="ClientValidationEnabled" value="true" />
      <add key="UnobtrusiveJavaScriptEnabled" value="true" />
      <add key="owin:AutomaticAppStartup" value="true" />
      <add key="ida:ClientId" value="someid" />
      <add key="ida:Tenant" value="mytenant.onmicrosoft.com" />
      <add key="ida:AADInstance" value="https://login.microsoftonline.com/{0}" />
    <add key="ida:PostLogoutRedirectUri" value="http://localhost/Default.aspx" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5.1" />
    <httpRuntime targetFramework="4.5.1" />     
    <authorization>
      <deny users="?" />      
    </authorization>
  </system.web>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />   
  </system.webServer>
  <location path="Default.aspx">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location> 
</configuration>

EDIT1
So regardless of if i have code or static pages inside child application, the authenticated user cannot access child application. I think this is a bug in OWIN or IIS, can someone please confirm this?

EDIT2
So after digging for several hours i am somewhat closer to solution.

1> I am using Azure AD with OpenID Connect for Single Sign On authentication. The authentication mechanism creates cookie. In order for authentication cookie to cross application boundary ( even though 2nd application is child application in IIS) we still need to use same machine key to decrypt the cookie. In my case my second application is setup as child application (see IIS screenshot above) so i just need to add machine key into root application. (In old FormsAuthentication we had to do the same thing )

2> Most important thing is the child application needs to be .Net 4.5 or latter with OWIN and identity framework configured. It cannot be just folder with some static files. It needs to be proper .Net web application (and i hated this because i have several child applications that are WCF services developed using .Net 4. So now i have to convert all these applications into 4.5 and configure OWIN & Identity framework and test. Really???)
With old FormsAuthentication we dont have to do this. The Authorization element was able to protect child applications and allow access when user was authenticated, regardless if child application contains .net app, wcf service, static html files or images.

回答1:

So EDIT2 above is my answer
You have to have Machine Key in web.config of main application. ( All child applications will inherit it)

And all child applications must be configured for OWIN (same as Main application. they must match clientid, tenant etc)