aspnetcore-angular-universal icon indicating copy to clipboard operation
aspnetcore-angular-universal copied to clipboard

Windows Auth Breaks HttpCacheService

Open yanbu0 opened this issue 8 years ago • 22 comments

Checking the "Enable Windows Authentication" checkbox in Debug properties in VS 2015 breaks the HttpCacheService. To replicate, simply check the box, run debug and navigate to the rest-test page. Unchecking the box removes the error, and replacing httpCache with http remvoes the error.

The error from Node is: Exception: Call to Node module failed with error: Response with status: 401 null for URL: Microsoft.AspNetCore.NodeServices.HostingModels.HttpNodeInstance+<InvokeExportAsync>d__7.MoveNext()

yanbu0 avatar Feb 28 '17 23:02 yanbu0

Can you try something else for me, go to Views/Home/Index.cshtml and remove the asp-prerender="bootstrap-server" attribute from the <root-app> tag?

I'm wondering if Windows Auth is breaking the entire App, or if it's something during the SSR (server-side rendering) part that it's breaking. Let me know!

MarkPieszak avatar Mar 01 '17 13:03 MarkPieszak

Yes, removing the asp-prerender-module="Client/dist/main-server" attribute also makes the error go away.

yanbu0 avatar Mar 01 '17 19:03 yanbu0

But it is not solution because we need pre-rendering

AbrarJahin avatar Mar 02 '17 02:03 AbrarJahin

True

yanbu0 avatar Mar 02 '17 17:03 yanbu0

Come via 4.1+ there will be an easier way to handle this. (State transfer method).

I'll leave this open as I haven't added HttpCacheService back in to see if it is still broken!

MarkPieszak avatar Apr 04 '17 18:04 MarkPieszak

I have rewritten the Cache service as it's now HttpTransfer, want to give this another shot and see if it works with it now? Not sure why windows Auth breaks this regardless though 😞

MarkPieszak avatar Apr 14 '17 14:04 MarkPieszak

Thanks Mark! Its going to be a bit of work getting everything in my project updated to ng4, but I will update this thread with results.

yanbu0 avatar Apr 14 '17 17:04 yanbu0

Sounds good I can give it a shot with the base repo eventually as well, no worries!

MarkPieszak avatar Apr 14 '17 18:04 MarkPieszak

Mark,

I tested windows auth with the base repo today and it appears to work fine with TransferHttp.

Was a quick test, and I simply turned it on and dropped this code into the UsersController to validate it was pulling data from AD:

string login = User.Identity.Name;

I will be converting my project over to Angular 4+ here this week using this template, and will update further once things are working.

yanbu0 avatar Apr 17 '17 18:04 yanbu0

Excellent glad it's working! 🎁👍 That's great news. I still some cosmetic things to do with the vendor chunking and such for webpack but those will be easy for you cherry pick and add to your app when upgrading.

I'll close this one out for other, if it creeps up again we can re-investigate it. Cheers

MarkPieszak avatar Apr 17 '17 19:04 MarkPieszak

Hi Mark, sorry about reporting this fixed earlier but it is actually still broken. Now it is just failing silently and falling back to querying the data from the client side automatically. What appears to be happening is that Node is unable authenticate and that causes your code to cause the client to query from that side. I have replicated this issue in my own environment and thought it was something I broke, but I just replicated it in a new copy of the repro today as well, so it appears to be an issue with the transfer-http module itself. The only modification I made was to turn on windows auth.

You can see here that transfer-http is failing back silently to querying from the client, note the call to /api/users third from the bottom: image

And here you can see the window['TRANSFER_STATE'] variable is not getting set properly: image

Here is the same screenshot, with no changes other than windows auth being turned off: image

The error from Node in the Output window is as follows: Microsoft.AspNetCore.NodeServices:Error: ERROR Response { _body: '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> \n<html xmlns="http://www.w3.org/1999/xhtml"> \n<head> \n<title>IIS 10.0 Detailed Error - 401.2 - Unauthorized</title> \n<style type="text/css"> \n<!-- \nbody{margin:0;font-size:.7em;font-family:Verdana,Arial,Helvetica,sans-serif;} \ncode{margin:0;color:#006600;font-size:1.1em;font-weight:bold;} \n.config_source code{font-size:.8em;color:#000000;} \npre{margin:0;font-size:1.4em;word-wrap:break-word;} \nul,ol{margin:10px 0 10px 5px;} \nul.first,ol.first{margin-top:5px;} \nfieldset{padding:0 15px 10px 15px;word-break:break-all;} \n.summary-container fieldset{padding-bottom:5px;margin-top:4px;} \nlegend.no-expand-all{padding:2px 15px 4px 10px;margin:0 0 0 -12px;} \nlegend{color:#333333;;margin:4px 0 8px -12px;_margin-top:0px; \nfont-weight:bold;font-size:1em;} \na:link,a:visited{color:#007EFF;font-weight:bold;} \na:hover{text-decoration:none;} \nh1{font-size:2.4em;margin:0;color:#FFF;} \nh2{font-size:1.7em;margin:0;color:#CC0000;} \nh3{font-size:1.4em;margin:10px 0 0 0;color:#CC0000;} \nh4{font-size:1.2em;margin:10px 0 5px 0; \n}#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS",Verdana,sans-serif; \n color:#FFF;background-color:#5C87B2; \n}#content{margin:0 0 0 2%;position:relative;} \n.summary-container,.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;} \n.content-container p{margin:0 0 10px 0; \n}#details-left{width:35%;float:left;margin-right:2%; \n}#details-right{width:63%;float:left;overflow:hidden; \n}#server_version{width:96%;_height:1px;min-height:1px;margin:0 0 5px 0;padding:11px 2% 8px 2%;color:#FFFFFF; \n background-color:#5A7FA5;border-bottom:1px solid #C1CFDD;border-top:1px solid #4A6C8E;font-weight:normal; \n font-size:1em;color:#FFF;text-align:right; \n}#server_version p{margin:5px 0;} \ntable{margin:4px 0 4px 0;width:100%;border:none;} \ntd,th{vertical-align:top;padding:3px 0;text-align:left;font-weight:normal;border:none;} \nth{width:30%;text-align:right;padding-right:2%;font-weight:bold;} \nthead th{background-color:#ebebeb;width:25%; \n}#details-right th{width:20%;} \ntable tr.alt td,table tr.alt th{} \n.highlight-code{color:#CC0000;font-weight:bold;font-style:italic;} \n.clear{clear:both;} \n.preferred{padding:0 5px 2px 5px;font-weight:normal;background:#006633;color:#FFF;font-size:.8em;} \n--> \n</style> \n \n</head> \n<body> \n<div id="content"> \n<div class="content-container"> \n <h3>HTTP Error 401.2 - Unauthorized</h3> \n <h4>You are not authorized to view this page due to invalid authentication headers.</h4> \n</div> \n<div class="content-container"> \n <fieldset><h4>Most likely causes:</h4> \n <ul> \t<li>No authentication protocol (including anonymous) is selected in IIS.</li> \t<li>Only integrated authentication is enabled, and a client browser was used that does not support integrated authentication.</li> \t<li>Integrated authentication is enabled and the request was sent through a proxy that changed the authentication headers before they reach the Web server.</li> \t<li>The Web server is not configured for anonymous access and a required authorization header was not received.</li> \t<li>The "configuration/system.webServer/authorization" configuration section may be explicitly denying the user access.</li> </ul> \n </fieldset> \n</div> \n<div class="content-container"> \n <fieldset><h4>Things you can try:</h4> \n <ul> \t<li>Verify the authentication setting for the resource and then try requesting the resource using that authentication method.</li> \t<li>Verify that the client browser supports Integrated authentication.</li> \t<li>Verify that the request is not going through a proxy when Integrated authentication is used.</li> \t<li>Verify that the user is not explicitly denied access in the "configuration/system.webServer/authorization" configuration section.</li> \t<li>Check the failed request tracing logs for additional information about this error. For more information, click <a href="http://go.microsoft.com/fwlink/?LinkID=66439">here</a>. </li> </ul> \n </fieldset> \n</div> \n \n<div class="content-container"> \n <fieldset><h4>Detailed Error Information:</h4> \n <div id="details-left"> \n <table border="0" cellpadding="0" cellspacing="0"> \n <tr class="alt"><th>Module</th><td>&nbsp;&nbsp;&nbsp;IIS Web Core</td></tr> \n <tr><th>Notification</th><td>&nbsp;&nbsp;&nbsp;AuthenticateRequest</td></tr> \n <tr class="alt"><th>Handler</th><td>&nbsp;&nbsp;&nbsp;aspNetCore</td></tr> \n <tr><th>Error Code</th><td>&nbsp;&nbsp;&nbsp;0x80070005</td></tr> \n \n </table> \n </div> \n <div id="details-right"> \n <table border="0" cellpadding="0" cellspacing="0"> \n <tr class="alt"><th>Requested URL</th><td>&nbsp;&nbsp;&nbsp;http://localhost:53170/api/users</td></tr> \n <tr><th>Physical Path</th><td>&nbsp;&nbsp;&nbsp;C:\\Users\\travis.boyle\\Documents\\Visual Studio 2017\\Projects\\aspnetcore-angular2-universal-master\\api\\users</td></tr> \n <tr class="alt"><th>Logon Method</th><td>&nbsp;&nbsp;&nbsp;Not yet determined</td></tr> \n <tr><th>Logon User</th><td>&nbsp;&nbsp;&nbsp;Not yet determined</td></tr> \n <tr class="alt"><th>Request Tracing Directory</th><td>&nbsp;&nbsp;&nbsp;C:\\Users\\travis.boyle\\Documents\\IISExpress\\TraceLogFiles\\ASP2017</td></tr> \n </table> \n <div class="clear"></div> \n </div> \n </fieldset> \n</div> \n \n<div class="content-container"> \n <fieldset><h4>More Information:</h4> \n This error occurs when the WWW-Authenticate header sent to the Web server is not supported by the server configuration. Check the authentication method for the resource, and verify which authentication method the client used. The error occurs when the authentication methods are different. To determine which type of authentication the client is using, check the authentication settings for the client. \n <p><a href="http://go.microsoft.com/fwlink/?LinkID=62293&amp;IIS70Error=401,2,0x80070005,15063">View more information &raquo;</a></p> \n <p>Microsoft Knowledge Base Articles:</p> \n <ul><li>907273</li><li>253667</li></ul> \n \n </fieldset> \n</div> \n</div> \n</body> \n</html> \n', status: 401, ok: false, statusText: 'Unauthorized', headers: Headers { _headers: Map { 'cache-control' => [Object], 'content-type' => [Object], 'server' => [Object], 'x-sourcefiles' => [Object], 'www-authenticate' => [Object], 'x-powered-by' => [Object], 'date' => [Object], 'content-length' => [Object] }, _normalizedNames: Map { 'cache-control' => 'cache-control', 'content-type' => 'content-type', 'server' => 'server', 'x-sourcefiles' => 'x-sourcefiles', 'www-authenticate' => 'www-authenticate', 'x-powered-by' => 'x-powered-by', 'date' => 'date', 'content-length' => 'content-length' } }, type: 2, url: 'http://localhost:53170/api/users' }

yanbu0 avatar Jun 21 '17 20:06 yanbu0

This actually appears to be a bug in Universal not specific to this repo.

yanbu0 avatar Jun 30 '17 22:06 yanbu0

Thank you for the extra details, do you have a sample repo I can look at that has everything setup so I can take a look?

Even just a fork of this with the windows Auth bug reproducing?

MarkPieszak avatar Jul 01 '17 16:07 MarkPieszak

So I did a ton of looking into this last week and it actually appears to be a bug in Universal itself, or at least an issue with it. Despite all appropriate headers being passed (best I can tell), windows auth fails from Node to Web Api, even with a straight http.get() call. It is not contained to your transferHttp methods. I created an issue on their github and linked it over to this one. I really hope someone can get this to work since the server>client data transfer is why I went with Angular on this project so I'm going to be kinda holding the bag if this flat out will not work!

I will try to create a fork that reproduces this today, but if you want to reproduce this all you have to do is turn on windows auth and drop an [Authorize] decorator on the web api controller.

yanbu0 avatar Jul 03 '17 16:07 yanbu0

OK, created a fork: https://github.com/yanbu0/aspnetcore-angular2-universal

I can reproduce the error like so: clone locally, run npm install, webpack, then open by right clicking on the csproj and opening in VS2017, make sure windows auth is enabled by right clicking on the project and checking the boxes like so: image

Then, debug. Navigate to the /users route and you will see the error occur. You will get the below in the console from Node if you refresh the page.

image

yanbu0 avatar Jul 03 '17 16:07 yanbu0

So the problem here is that during the server-rendering, the Http calls in the app aren't setup to pass the correct Authentication needed. You need to make sure you pass those from HomeController -> main.server into some sort of Provider that you can inject into your Http headers during the server-render.

The issue is that you're also going to have to also pass this down to transferData so that your browser can grab them and use these cookies as well (and once again inject them into the Header).

If you remove Server-side rendering it'll make the process simpler, otherwise you'll have to use a little bit of dependency injection magic to make it all happen.

MarkPieszak avatar Jul 05 '17 12:07 MarkPieszak

@MarkPieszak,

OK, I'm pretty confused now. What do you mean when you say "pass the correct Authentication needed"? It looks like the server side request has all the correct headers. See screenshot below. Are you talking about something else? image

yanbu0 avatar Jul 07 '17 22:07 yanbu0

Sorry I meant do you have your http calls setup to use those headers in each of their calls during the server render?

MarkPieszak avatar Jul 08 '17 03:07 MarkPieszak

@yanbu0 is this still an issue?

isaacrlevin avatar Nov 13 '17 02:11 isaacrlevin

I figured out a work around, but yeah, Windows Auth seems to not work very well still. I'm planning on updating to Ng 5 here shortly and will see if that fixes things.

yanbu0 avatar Nov 13 '17 05:11 yanbu0

@yanbu0 can you provide your workaround? 5.0 has issues so you won't get far, but 4.X fix would be helpful

isaacrlevin avatar Jan 19 '18 16:01 isaacrlevin

Yeah, upgrading to 5 hasn't happened yet, was breaking stuff, so I just went back to 4.x, and am waiting for the dust to settle.

Anyway, what I figured out is that the server side was always failing authentication, but the client side was not.

So I let the server side fail, the initial paint had no permissions or data, which sucks, but then create a token on the client side after the user authenticated and passed that in the http header for all future requests.

The initial server-side auth fails, but all other ones fall back to using the token if they can't grab the user from the context, and then pull the user permissions from the database. Kind of a hybrid windows auth/token auth setup. It seems kind of a hack to me and I don't like it, but I spent weeks fighting this problem and this was the only way I could get things to function.

I don't use AD groups for permissions, we have a separate, app specific, permission structure, so all I need to check to see if a user can perform an action is who they are, and that they are authorized to log in, so it works for me.

Honestly, I'm kind of frustrated with Angular at this point and am starting to wish I had gone another direction. Most of the reason I went with it over React, Vue, or some other framework was because it was an all in one package deal (and Google's name on it). I figured getting all the server side rendering, AoT, and whatnot working would be more straightforward with Angular since it was a single framework. I still can't get AoT to work and the server side rendering usefulness has been neutered by this issue. On top of that, the massive package size of the transpiled .js files is causing real usability issues.

yanbu0 avatar Jan 19 '18 17:01 yanbu0