Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

1,072 views
Skip to first unread message

mark Kharitonov

unread,
Jun 27, 2011, 8:54:29 AM6/27/11
to open...@googlegroups.com
Given:
  • A resource with basic authentication enabled.
  • ASP.NET hosted inside IIS7
  • I am trying to access the resource
Outcome:
Fiddler reports an infinite loop:
#ResultProtocolHostURLBodyCachingContent-TypeProcessCommentsCustom
3401HTTPlocalhost/MyRestApplication/articles5 418private text/html; charset=utf-8iexplore:1312
4401HTTPlocalhost/MyRestApplication/articles341text/html; charset=us-asciiiexplore:1312
5401HTTPlocalhost/MyRestApplication/articles5 428private text/html; charset=utf-8iexplore:1312
6401HTTPlocalhost/MyRestApplication/articles341text/html; charset=us-asciiiexplore:1312
7401HTTPlocalhost/MyRestApplication/articles5 428private text/html; charset=utf-8iexplore:1312
8401HTTPlocalhost/MyRestApplication/articles341text/html; charset=us-asciiiexplore:1312
... goes on ad infinitum ...

Here is the details of the first two requests/responses, the ones which get endlessly repeated:
Request 1:
GET http://localhost/MyRestApplication/articles HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: fr-FR
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.100)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost

Reply 1:
HTTP/1.1 401 Unauthorized
Cache-Control: private
Content-Type: text/html; charset=utf-8
Server: Microsoft-IIS/7.5
WWW-Authenticate: Basic realm="My REST application"
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Mon, 27 Jun 2011 12:35:20 GMT
Content-Length: 5418

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
...

Request 2:
GET http://localhost/MyRestApplication/articles HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: fr-FR
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; chromeframe/12.0.742.100)
Accept-Encoding: gzip, deflate
Connection: Keep-Alive
Host: localhost
Authorization: Negotiate YH0GBisGAQUFAqBzMHGgMDAuBgorBgEEAYI3AgIKBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHqI9BDtOVExNU1NQAAEAAACXsgjiCQAJADIAAAAKAAoAKAAAAAYBsR0AAAAPSUwtTUFSSy1MVFNIVU5SQU5FVA==

Reply 2:
HTTP/1.1 401 Unauthorized
Content-Type: text/html; charset=us-ascii
Server: Microsoft-HTTPAPI/2.0
WWW-Authenticate: Negotiate oYIBAjCB/6ADCgEBoQwGCisGAQQBgjcCAgqigekEgeZOVExNU1NQAAIAAAASABIAOAAAABXCieLQ11dZMMca99CBsAEAAAAAnACcAEoAAAAGAbEdAAAAD1MASABVAE4AUgBBAE4ARQBUAAIAEgBTAEgAVQBOAFIAQQBOAEUAVAABABQASQBMAC0ATQBBAFIASwAtAEwAVAAEABQAcwBoAHUAbgByAGEALgBuAGUAdAADACoASQBMAC0ATQBhAHIAawAtAEwAVAAuAHMAaAB1AG4AcgBhAC4AbgBlAHQABQAUAHMAaAB1AG4AcgBhAC4AbgBlAHQABwAIAKajf63GNMwBAAAAAA==
Date: Mon, 27 Jun 2011 12:35:20 GMT
Content-Length: 341
Proxy-Support: Session-Based-Authentication

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
<HTML><HEAD><TITLE>Not Authorized</TITLE>
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
<BODY><h2>Not Authorized</h2>
<hr><p>HTTP Error 401. The requested resource requires user authentication.</p>
</BODY></HTML>

I understand that the problem is that IIS7 appends additional WWW-Authenticate response headers suggesting Negotiate and NTLM schemes in addition to the Basic sent by my application and then Internet Explorer 9 automatically selects the Negotiate scheme.

This does not happen when the application is hosted in Cassini, nor does it occur when I access it using Firefox instead of IE.

Does anyone know how to fix it?

Thanks.

P.S.
I have just realized this is not an OpenRasta issue...

Sebastien Lambla

unread,
Jun 27, 2011, 9:25:04 AM6/27/11
to open...@googlegroups.com

Disable “integrated authentication” in IIS and it won’t add the other headers.

mark Kharitonov

unread,
Jun 27, 2011, 10:24:34 AM6/27/11
to open...@googlegroups.com
OK, it works.
But it took two to screw the process. Even when IIS add the headers, Firefox continue to work correctly because it takes the first WWW-Authorize header, which is basic.Internet Explorer, on the other hand, prefers to take the Negotiate, even if it is not the first header.

Which one is correct? Does the HTTP standard clearly states which header wins? Can IE be instructed to behave like Firefox?

Thanks.

2011/6/27 Sebastien Lambla <s...@serialseb.com>



--
Be well and prosper.
==============================
"There are two kinds of people.Those whose guns are loaded and those who dig."
   ("The good, the bad and the ugly")
So let us drink for our guns always be loaded.

Sebastien Lambla

unread,
Jun 27, 2011, 10:44:54 AM6/27/11
to open...@googlegroups.com

You can prioritize them but as IIS adds no q= there’s nothing you can do to get basic over Negotiate. IE is free to do as it wish in selecting which authentication scheme to use anyway, as long as the server advertise it.

mark Kharitonov

unread,
Jun 27, 2011, 11:38:56 AM6/27/11
to open...@googlegroups.com
I am trying to understand the root cause of the problem.
IIS adds two more WWW-Authenticate headers - Negotiate and NTLM, all of which have to do with windows authentication. This must mean that IIS believes that windows authentication is possible. Apparently, my server does not support windows authentication, while IIS and IE both believe that it should. I would like to fix it.

How do I support windows authentication in OR?

mark Kharitonov

unread,
Jun 27, 2011, 12:07:42 PM6/27/11
to open...@googlegroups.com
My motivation is this - I am trying to write a Silverlight client to work against my REST web service. Silverlight client must be served by a web server, I can write another ASP.NET app just to serve the Silverlight client or I can reuse the already existing ASP.NET app, which hosts my REST web service. But there is a catch! In order to serve the Silverlight client, IIS seems to require windows authentication!

Sebastien Lambla

unread,
Jun 27, 2011, 12:12:29 PM6/27/11
to open...@googlegroups.com

No no no. The way HTTP auth works is that you can have multiple authentication types that are available. OpenRasta has Basic, IIS has its own Basic, and iwndows integrated (aka negotiate and ntlm). If you *disable* windows auth in IIS, then the client will pick the authentication provided by OpenRasta. If you *enable* windows auth in IIS, then IIS will do the authentication and OpenRasta has no control over that at all.

 

If you disable win auth in IIS then the problem goes away. If you enable win auth in IIS, then IE will use that and you need to make sure users have accounts in whatever direcetory service your IIS box uses to authenticate windows users (usually your windows server).

 

So there’s not much to fix in what you’ve written except for removing the option from IIS.

Sebastien Lambla

unread,
Jun 27, 2011, 12:13:14 PM6/27/11
to open...@googlegroups.com

If you don’t protect the xap file then no, you don’t need window sauthetnication or any authentication to serve that xap file. If you require authentication on the xap file, then it’s the same as for your service.

mark Kharitonov

unread,
Jun 27, 2011, 3:50:58 PM6/27/11
to open...@googlegroups.com
Thanks for the explanations, but it still does not explain the infinite recursion behavior. Both IIS and IE are run from the same machine - mine dev workstation. My user is an ordinary domain user and this is the first time I witness such behavior.

How can I trace it down? I do not want to let it slip away unnoticed, since it may later return with a vengeance. Are there any logs that I can examine to understand what is going on?

Thank you very much for your patience.

2011/6/27 Sebastien Lambla <s...@serialseb.com>

Sebastien Lambla

unread,
Jun 27, 2011, 4:03:25 PM6/27/11
to open...@googlegroups.com

There’s really no infinite recursion, there’s an infinite access attempt from the client after multiple unsuccessful attemps, which is a bug in the client code and you’ll have to take it with whoever wrote the client or the underlying http connection (which may be any of Silverlight’s http client, the browser client, the chrome frame plugin or the code making use of that code randomly repeating over and over again).

 

Either OR doesn’t authorize the request, or IIS doesn’t authenticate because it doesn’t know the credentials or something.

 

You can check the reason for the 401 in the IIS logs, it’ll tell you the subcode. You can check the openrasta log if you enable logging (which you can get in the debugger if you’re attached, or configure through trace sources) and it’ll tell you if it’s responsible for the response (I don’t think OR even gets called at all from the shape of your log file).

mark Kharitonov

unread,
Jun 27, 2011, 4:19:10 PM6/27/11
to open...@googlegroups.com
Here is the given:
  • Silverlight is out of the picture. I have just added it in order to explore my abilities as a REST client from Silverlight after observing the reported behavior.
  • The OR web service is really tiny and trivial, mostly the example from the tutorial with basic authentication. I can zip the project source and post here, if needed, but there is really nothing there.
  • No error subcode in IIS log:
2011-06-27 20:10:11 ::1 GET /MyRestApplication/articles.json - 80 SHUNRANET\markk ::1 Mozilla/5.0+(compatible;+MSIE+9.0;+Windows+NT+6.1;+WOW64;+Trident/5.0) 401 0 0 8
  • OR is called multiple time. In fact each Request-Reply-Request-Reply cycle corresponds to one InterceptorException.A better way to describe the cycle is this: Request-InterceptorException-Reply-Request-Reply and so on. The debug output is attached as 7z archive.
Thanks again.

2011/6/27 Sebastien Lambla <s...@serialseb.com>
log.7z

Sebastien Lambla

unread,
Jun 27, 2011, 5:14:45 PM6/27/11
to open...@googlegroups.com

Let me try to explain based on your logs:

1.       Client sends unauthenticated request. OpenRasta sees that proper authorization is not available (user is authenticated) and returns  a 401. IIS adds its own “available authentication scheme” on top of the OR ones

2.       Client choses the IIS authentication scheme (as it’s allowed to do) and reissue the request. IIS reads that and authenticates the user, creating an IPrincipal that OpenRasta will now use to decide if the user is authorized for the method. OpenRasta sees that the windows user in question *does not* have the correct authorization for that resource, and issues another challenge.

3.       Client receive the 401, choses the IIS authentication scheme (as it’s allowed to do) and reissue the request. IIS (see point 2).

4.       Rinse, repeat, advitam eternam.

 

As I expressed before, OpenRasta is doing the right thing. IIS is doing the right thing. The client *may* be doing the right thing but I doubt it:

10.4.2 401 Unauthorized

The request requires user authentication. The response MUST include a WWW-Authenticate header field (section 14.47) containing a challenge applicable to the requested resource. The client MAY repeat the request with a suitable Authorization header field (section 14.8). If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials. If the 401 response contains the same challenge as the prior response, and the user agent has already attempted authentication at least once, then the user SHOULD be presented the entity that was given in the response, since that entity might include relevant diagnostic information. HTTP access authentication is explained in "HTTP Authentication: Basic and Digest Access Authentication" [43].

 

In effect, either the client (the http lib in use) does not understand the whole point of not restarting if it’s the same challenge, or maybe IIS returns a different challenge everytime, which I don’t know about because only Microsoft implements that stuff and no one cares much for implementing it independently on the server.

 

Now, that’s not even half of the problem, because if you look at your log files you’ll see that the first and the second responses don’t come from the same server component: The first response comes from IIS (because it was an unchallenged request, OR replied, and IIS didn’t involve the http listener very much). But because the http listener underneath all sees a perfectly well authenticated connection (both because the client doesn’t understand that a positive response in a WWW-Authenticate combined with a 401 should still fail those credentials, and beause the host is dumb enough to continue with an authenticated response even though a higher level framework specifically said it was a 401), everyone underneath gets very confused, as they are very much used to owning everything and having only dim-witted frameworks built on top of them.

 

Sooo all this to say: the client is not doing the right thing, the http.sys component is not either, IIS7.5 itself has little to do with it all and OpenRasta has absolutely no way to influence any outcome in that environment, as the IIS components have handled the whole shebang of authentication without asking anyone and without listening to anyone.

 

So you have two solutions. Either you switch to windows authentication and you leave OpenRasta with authorizing strictly (aka return a 403 in the knowledge that no browser using NEGOTIATE will ever ask you for different credentials, which is the whole point of http authentication being a challenge/response system, but I disgress), or you disable authentication in IIS so OpenRasta can do its job. There are no other alternatives.

mark Kharitonov

unread,
Jun 28, 2011, 3:23:31 AM6/28/11
to open...@googlegroups.com
Thank you very much for the detailed analysis.

I must be stupid, because I do not understand how "OpenRasta sees that the windows user in question *does not* have the correct authorization for that resource, and issues another challenge"
I placed a breakpoint in OpenRasta.Hosting.AspNet.OpenRastaHandler.GetHandler. It is not called for the second request, the one containing the Negotiate authentication scheme. It is as though the second request gets responded before it is ever dispatched to OpenRasta.

So, what I do not understand is how can OR see anything from the second request?

Thanks.

P.S.
I am attaching the 7z archive of my web service. It is really minuscule.


2011/6/28 Sebastien Lambla <s...@serialseb.com>
1.7z

Sebastien Lambla

unread,
Jun 28, 2011, 5:05:14 AM6/28/11
to open...@googlegroups.com

Hmm. Either OR was called and the user was the windows authenticated one, or the user in question is not authorized and then it’s IIS / http.sys that answers before dispatching to us. Either way it won’t work, that’s what I’m trying to get to :)

mark Kharitonov

unread,
Jun 28, 2011, 5:14:26 AM6/28/11
to open...@googlegroups.com
I see that OR is not called for the second request. What is the definitive procedure to make sure or having the breakpoint set in OpenRasta.Hosting.AspNet.OpenRastaHandler.GetHandler is good enough? If it is, then I confirm - no OR invocation.

Now, who is the user in question? Is it the logged in user? What is the meaning of "not authorized"? Not authorized to do what? Is there a setting in IIS that I should examine to make sure the proper authorizations are in place?

Again, thank you for the patience.

2011/6/28 Sebastien Lambla <s...@serialseb.com>

Scott Littlewood

unread,
Jun 28, 2011, 12:37:36 PM6/28/11
to open...@googlegroups.com
With regards to your:  What is the meaning of "not authorized"?
This is taken from the http spec, it actually means 'Not authenticated' i.e. we don't know this person/credentials combination.

Not that the person making the request doesn't have permission to perform an operation.

There is blurring between Authenticate and Authorize.

Hope this doesn't confuse.

With regards to your deployment problems in IIS I've had OR basic auth working behind IIS by disabling IIS authorization schemes.

Mark Kharitonov

unread,
Jun 28, 2011, 5:25:15 PM6/28/11
to open...@googlegroups.com
I am new to the world of web services and I am trying to understand how the things work. Now, here is what I have:
  • A resource without any authentication is served just fine with IIS/Cassini on the server side and IE/Firefox - on the client.
  • A resource with basic authentication is served just fine with IIS/Cassini on the server side and Firefox - on the client.
  • A resource with basic authentication is served just fine with Cassini on the server side and IE/Firefox - on the client.
  • A resource with basic authentication is served just fine with IIS on server side and IE - on the client, provided windows authentication is disabled for the particular ASP.NET application in IIS.
  • The problematic scenario is when:
    • A resource with basic authentication is served
    • IIS on server side
    • IE on client side
    • Windows authentication is enabled for the particular ASP.NET application in IIS.
I would really like to know what is wrong in the last scenario. How does one troubleshoot things like this? There has to be a way.
==========================================================================
There are two kinds of people. Those whose guns are loaded and those who dig.
(The good, the bad and the ugly).
So let us raise our cups for our guns always be loaded.


Sebastien Lambla

unread,
Jun 28, 2011, 7:55:35 PM6/28/11
to open...@googlegroups.com

I’m not sure how to explain it any further. Whenever IIS takes over authentication, we have no control over what the heck is going on, because that’s how MS built their infrastructure.

 

If things end up using NEGOTIATE, IIS does all the work.

 

If the client is providing a specific username and password, then negotiate will authenticate that against Active Directory and authenticate or not, based on whatever credentials are available on the Active Directory server.

 

If you don’t get authenticated (because the client sends credentials that only exist in the basic authentication in OpenRasta, and there are no matching username/password in active directory), then you get a 401, and the client re-issues over and over again (a bug in the client code).

 

If you do get authenticated, IIS retrieves the groups your windows account (the one you authenticated for with a username and password) and creates an IPrincipal for asp.net that tells you what windows groups that user is member of. This may or may not (and probably doesn’t) match what your authentication provider in OpenRasta would respond with.

 

That’s the authentication part, aka “who the hell are you and what groups are you part of”, and is represented by the IPrincipal and IIdentity interfaces part of .net. OpenRasta just assume that if you’re not anonymous, you have been authenticated by something else and use whatever IPrincipal/IIdientity comes from the hosting environment (IIS).

 

When *authorization* kicks in, which is *what are you allowed to see*, then OpenRasta use the IPrincipal/IIdentity couple to check if you’re allowed to do something (such as access a protected resource).  That’s based on the groups AD came back, and theres  nothing we can do about what authenthication was done by the underlying platform.

 

Let me reformulate. There is no way we can change what happens before OpenRasta gets hit, and the fix to prevent that is to configure the IIS properly to not allow IIS to do the authentication. What is it that you’re trying to “fix” that is not already fixed by the fix?

 

From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of Mark Kharitonov
Sent: 28 June 2011 22:25
To: open...@googlegroups.com
Subject: Re: [openrasta] Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

 

I am new to the world of web services and I am trying to understand how the things work. Now, here is what I have:

·         A resource without any authentication is served just fine with IIS/Cassini on the server side and IE/Firefox - on the client.

·         A resource with basic authentication is served just fine with IIS/Cassini on the server side and Firefox - on the client.

·         A resource with basic authentication is served just fine with Cassini on the server side and IE/Firefox - on the client.

·         A resource with basic authentication is served just fine with IIS on server side and IE - on the client, provided windows authentication is disabled for the particular ASP.NET application in IIS.

·         The problematic scenario is when:

o    A resource with basic authentication is served

o    IIS on server side

o    IE on client side

o    Windows authentication is enabled for the particular ASP.NET application in IIS.

mark Kharitonov

unread,
Jun 29, 2011, 9:42:30 AM6/29/11
to open...@googlegroups.com
First of all, I have found another way around this issue. Instead of disabling Windows Authentication in IIS, one can disable it in IE - http://blog.super-networking.net/2008/02/internet-explorer-enable-integrated-windows-authentication/.

Next, I will try to explain what I do not understand. I know the following facts:
  1. IE tries to negotiate with IIS between NTLM and Kerberos. This is the meaning of the Negotiate http authentication scheme.
  2. Everything happens on my local machine, which does not have Kerberos.
  3. This is the first web app hosted on my local IIS requiring authentication, so I have never encountered these issues before.
  4. I am logged in with my domain account - no problems in this department.
  5. The loop cycle goes like this:
    1. IE requests a page from OR. No Authorization headers are given.
    2. OR fails the request with 401 demanding basic authentication
    3. IIS adds on it two more WWW-Authenticate headers demanding Negotiate and NTLM authentication
    4. IE (having Integrated Windows Authentication enabled) picks the Negotiate scheme and responds with some authentication data.
    5. IIS responds to it with yet another Negotiate authentication data, without notifying OR at all. The status is still 401.
    6. At this point IE repeats the request (1), only this time it includes the Authorization: Negotiate header with some yet another data.
    7. Goto (2)
Obviously, the negotiation between IIS and IE fails and this is something that I do not understand and want to fix. But I am interested to fix it while keeping integrated windows authentication.


2011/6/29 Sebastien Lambla <s...@serialseb.com>

Sebastien Lambla

unread,
Jun 29, 2011, 10:22:30 AM6/29/11
to open...@googlegroups.com

Right, so I’ve had to refresh myself on how SPNEGO works (http://tools.ietf.org/html/rfc4559).

 

What you’re seeing is perfectly normal and the infinite loop is unavoidable.

 

The back and forth of GET / 401 is normal until the whole negotiation and exchange is done between client and server. At that point, IIS let the request go through as it considers the authentication to have succeeded. At that point, OR may receive the request with the principal provided by IIS (your switch from 7 to 2), and that hasn’t got the right permissions (as it’s a domain account, not the account you return from your credential provider in OR), so OR re-issues a 401, and IIS gets confused and re-initiate the negotiation.

 

There’s just no way to prevent this from happening, a new challenge will always be initiated when someone in the pipeline is saying the credentials presented don’t have access. In your case, that’s always hadnled by IIS, unless you disable windows authentication on that application.

 

I don’t think there’s any failing there, it’s just an awkward thing that is happening. I still think that considering, the client is the one with a bug as it doesn’t detect infinite redirects to start with.

 

From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of mark Kharitonov
Sent: 29 June 2011 14:43
To: open...@googlegroups.com
Subject: Re: [openrasta] Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

 

First of all, I have found another way around this issue. Instead of disabling Windows Authentication in IIS, one can disable it in IE - http://blog.super-networking.net/2008/02/internet-explorer-enable-integrated-windows-authentication/.



Next, I will try to explain what I do not understand. I know the following facts:

1.      IE tries to negotiate with IIS between NTLM and Kerberos. This is the meaning of the Negotiate http authentication scheme.

2.      Everything happens on my local machine, which does not have Kerberos.

3.      This is the first web app hosted on my local IIS requiring authentication, so I have never encountered these issues before.

4.      I am logged in with my domain account - no problems in this department.

5.      The loop cycle goes like this:

1.      IE requests a page from OR. No Authorization headers are given.

2.      OR fails the request with 401 demanding basic authentication

3.      IIS adds on it two more WWW-Authenticate headers demanding Negotiate and NTLM authentication

4.      IE (having Integrated Windows Authentication enabled) picks the Negotiate scheme and responds with some authentication data.

5.      IIS responds to it with yet another Negotiate authentication data, without notifying OR at all. The status is still 401.

6.      At this point IE repeats the request (1), only this time it includes the Authorization: Negotiate header with some yet another data.

7.      Goto (2)

mark Kharitonov

unread,
Jun 29, 2011, 10:32:08 AM6/29/11
to open...@googlegroups.com
At that point, OR may receive the request with the principal provided by IIS (your switch from 7 to 2), and that hasn’t got the right permissions (as it’s a domain account, not the account you return from your credential provider in OR)

Are you sure of that? Because when I debug the code ICommunicationContext.User is set to an empty GenericPrincipal instance. I would have expected to find something else there. Something to reflect the fact that the Negotiate authentication succeeded.

Sebastien Lambla

unread,
Jun 29, 2011, 10:48:49 AM6/29/11
to open...@googlegroups.com

I’d expect that too. What’s HttpContext.Current.User containing?

mark Kharitonov

unread,
Jun 29, 2011, 10:53:22 AM6/29/11
to open...@googlegroups.com
-        _context.User    {System.Security.Principal.GenericPrincipal}    System.Security.Principal.IPrincipal {System.Security.Principal.GenericPrincipal}
-        [System.Security.Principal.GenericPrincipal]    {System.Security.Principal.GenericPrincipal}    System.Security.Principal.GenericPrincipal
+        Identity    {System.Security.Principal.GenericIdentity}    System.Security.Principal.IIdentity {System.Security.Principal.GenericIdentity}
-        m_identity    {System.Security.Principal.GenericIdentity}    System.Security.Principal.IIdentity {System.Security.Principal.GenericIdentity}
-        [System.Security.Principal.GenericIdentity]    {System.Security.Principal.GenericIdentity}    System.Security.Principal.GenericIdentity
        AuthenticationType    ""    string
        IsAuthenticated    false    bool
        m_name    ""    string
        m_type    ""    string
        Name    ""    string
        AuthenticationType    ""    string
        IsAuthenticated    false    bool
        Name    ""    string
        m_roles    {string[0]}    string[]
+        Identity    {System.Security.Principal.GenericIdentity}    System.Security.Principal.IIdentity {System.Security.Principal.GenericIdentity}

Sebastien Lambla

unread,
Jun 29, 2011, 11:08:13 AM6/29/11
to open...@googlegroups.com

Is that the ICommunicationContext one? what about the one on HttpContext.Current.User?

 

From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of mark Kharitonov


Sent: 29 June 2011 15:53
To: open...@googlegroups.com

mark Kharitonov

unread,
Jun 29, 2011, 11:14:01 AM6/29/11
to open...@googlegroups.com
ICommunicationContext is actually AspNetCommunicationContext and AspNetCommunicationContext.User is implemented like so:
public IPrincipal User
{
  get { return NativeContext.User; }
  set { NativeContext.User = value; }
}

Where NativeContext is HttpContext.

So, the ICommunicationContext.User property is actually HttpContext.User.

Sebastien Lambla

unread,
Jun 29, 2011, 11:15:18 AM6/29/11
to open...@googlegroups.com

I see.

 

Well I’m not sure I know why the identity is not flowing then post-negotiation.

 

Wonder if the credential readers don’t try and reset them when the sceme is unkown, although that seems unlikely. Let me have a check.

 

From: open...@googlegroups.com [mailto:open...@googlegroups.com] On Behalf Of mark Kharitonov


Sent: 29 June 2011 16:14
To: open...@googlegroups.com

Sebastien Lambla

unread,
Jun 29, 2011, 11:28:11 AM6/29/11
to open...@googlegroups.com

I checked and there is nothing of the sort.If it’s a local server that you connect to, and there are no proxies, and the request goes back to OR the second time around, something is not reading the credentials properly on the asp.net side, and I would have no idea why.

 

I’d suggest doing an empty web application, protect it with windows integrated auth, and try to get some file, that may help you diagnose which config switch in IIS  you may be hitting

Mark Kharitonov

unread,
Jul 1, 2011, 4:26:03 PM7/1/11
to open...@googlegroups.com
Thanks.
I will be able to return to this in a few days and let you know about my findings.

mark Kharitonov

unread,
Jul 10, 2011, 9:44:37 AM7/10/11
to open...@googlegroups.com
OK, I have it.

There were two problems.
First, while trying to resolve the issue the following line crept in into web.config:

<authentication mode="None"/>


It did not stop windows authentication from happening, but it did nullified the principal passed to OR. So, although the client was authenticated (albeit by windows rather than basic authentication), the principal passed to the code was the unauthenticated principal.

So, first of all I changed it to

<authentication mode="Windows"/>

Deleting it also works, but I want it to be explicit.

Now, the principal is authenticated, but there is another problem.

The request was authenticated using the Negotiate scheme. But, when the AuthenticationContributor.AuthoriseRequest  method tries to locate the respective IAuthenticationScheme instance it finds none, because only the BasicAuthenticationScheme is registered. At this point the user is authenticated, but it has no application roles and so we fail in RequiresRoleInterceptor.

So, besides using Firefox instead of Internet Explorer or turning windows authentication off in IE or IIS, there is another solution - introduce NegotiateAuthenticationScheme.

Here we go:

using System.Web;
using OpenRasta.Authentication;
using OpenRasta.Web;

namespace MyRestApplication.Auth
{
  public class NegotiateAuthenticationScheme : IAuthenticationScheme
  {
    private readonly IUserRolesProvider m_userRolesProvider;
    private readonly IMapPrincipalToUser m_mapPrincipalToUser;
    const string Scheme = "Negotiate";

    public NegotiateAuthenticationScheme(IUserRolesProvider userRolesProvider, IMapPrincipalToUser mapPrincipalToUser)
    {
      m_userRolesProvider = userRolesProvider;
      m_mapPrincipalToUser = mapPrincipalToUser;
    }

    #region Implementation of IAuthenticationScheme

    public string Name
    {
      get { return Scheme; }
    }

    public AuthenticationResult Authenticate(IRequest request)
    {
      if (HttpContext.Current.User.Identity.AuthenticationType == Scheme)
      {
        // The user has already been authenticated by the web server using the
        // window authentication. OR did not have a say in it, because both the browser and the web
        // server did it "behind its back". How come? Observe:
        // - Browser is IE
        // - Web server is IIS
        // - OR added WWW-Authenticate: Basic
        // - IIS added WWW-Authenticate: Negotiate
        // - IIS added WWW-Authenticate: NTLM
        // - IE decided to ignore the basic authentication and went on the Negotiate, because it has the
        //   "Use Windows Authentication" security option enabled. In our case this is NTLM.
        // - IIS successfully authenticates the user using windows authentication and passes the request
        //   to OR
        // - OR gets the request with Authorization: Negotiate, rather than Authorization: Basic
        // - At this point the user has been successfully authenticated, but not with the basic authentication
        var user = m_mapPrincipalToUser.GetUserName(HttpContext.Current.User);
        return new AuthenticationResult.Success(user, m_userRolesProvider.GetUserRoles(user));
      }

      return new AuthenticationResult.Failed();
    }

    public void Challenge(IResponse response)
    {
    }

    #endregion
  }
}

It depends on two simple interfaces - IUserRolesProvider and  IMapPrincipalToUser:

  public interface IMapPrincipalToUser
  {
    string GetUserName(IPrincipal principal);
  }

  public interface IUserRolesProvider
  {
    string[] GetUserRoles(string userName);
  }

Now the following code goes into Configuration.cs:

ResourceSpace.Uses.CustomDependency<IAuthenticationScheme, BasicAuthenticationScheme>(DependencyLifetime.Singleton);

ResourceSpace.Uses.CustomDependency<IAuthenticationScheme, NegotiateAuthenticationScheme>(DependencyLifetime.Singleton);
ResourceSpace.Uses.CustomDependency<IMapPrincipalToUser, MapPrincipalToUser>(DependencyLifetime.Singleton);
ResourceSpace.Uses.CustomDependency<IBasicAuthenticator, MyBasicAuthenticator>(DependencyLifetime.Transient);
ResourceSpace.Uses.CustomDependency<IUserRolesProvider, UserRolesProvider>(DependencyLifetime.Transient);

Where MyBasicAuthenticator also depends on IUserRolesProvider.

That's it.

What do you think, falks?
Message has been deleted

mark Kharitonov

unread,
Jul 10, 2011, 11:17:24 AM7/10/11
to open...@googlegroups.com
I mean folks, of course.

mark Kharitonov

unread,
Jul 12, 2011, 10:17:01 AM7/12/11
to open...@googlegroups.com
Anyone?

Sebastien Lambla

unread,
Jul 12, 2011, 10:31:51 AM7/12/11
to open...@googlegroups.com
I'm not sure in this case that you should be adding an auth scheme, as technically you're not actually doing authentication, you're doing authorization after authentication has been done. With this, OpenRasta will think it's handling authentication, and will add the negotiate scheme to the list of potential schemes it can handle, which would be a bit iffy.

I'd suggest instead to be writing a pipeline contributor and simply changing the Identity on the ICommunicationContext.User property to an instance of GenericPrincipal.

As for the code itself: The check on the authentication type shouldn't return a Failed when it doesn't work, as you've not actually failed authentication, it's just an authetnication you don't know about.

Furthermore, take a dependency on ICommunicationContext rather than HttpContext, or you'll be stuck forever on asp.net as a hosting environment.

As for getting the username from an IPrincipal, ther'es already an IIdentity that gives you just that, so I'd suggest removing IMapPrincipalToUser, unless you have some custom code in there instead of just using windows accounts.

Your dependency registrations also register your scheme aas a singleton, but has a depenndency on something registered as a transient, which is going to give you not what you expect.



 


From: open...@googlegroups.com [open...@googlegroups.com] on behalf of mark Kharitonov [mark.kh...@gmail.com]
Sent: 10 July 2011 14:44
To: open...@googlegroups.com
Subject: Re : Re: Re : RE: Re : RE: Re : RE: [openrasta] Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

mark Kharitonov

unread,
Jul 12, 2011, 11:46:48 AM7/12/11
to open...@googlegroups.com
Please, find my response below in red:

2011/7/12 Sebastien Lambla <s...@serialseb.com>

I'm not sure in this case that you should be adding an auth scheme, as technically you're not actually doing authentication, you're doing authorization after authentication has been done. With this, OpenRasta will think it's handling authentication, and will add the negotiate scheme to the list of potential schemes it can handle, which would be a bit iffy.

I'd suggest instead to be writing a pipeline contributor and simply changing the Identity on the ICommunicationContext.User property to an instance of GenericPrincipal.
 
OK, I will try to introduce a dedicated pipeline contributor for that.
 
As for the code itself: The check on the authentication type shouldn't return a Failed when it doesn't work, as you've not actually failed authentication, it's just an authetnication you don't know about.

Furthermore, take a dependency on ICommunicationContext rather than HttpContext, or you'll be stuck forever on asp.net as a hosting environment. 
 
No can do. I tried that first, but ICommunicationContext.User is still null at that time. I do not like the dependency on asp.net, though, so I will be happy to get another suggestion.

As for getting the username from an IPrincipal, ther'es already an IIdentity that gives you just that, so I'd suggest removing IMapPrincipalToUser, unless you have some custom code in there instead of just using windows accounts.
 
There is a difference. Basic authentication defines application users, whereas WindowsIdentity corresponds to OS accounts. The application logic revolves around application users and groups, so OS accounts must be mapped somehow to application users - enter IMapPrincipalToUser.

Your dependency registrations also register your scheme aas a singleton, but has a depenndency on something registered as a transient, which is going to give you not what you expect.

You are right , of course.

Sebastien Lambla

unread,
Jul 13, 2011, 8:34:28 AM7/13/11
to open...@googlegroups.com
Hmmm. ICommunicationContext.User should never be null, whatever happens. If the hosting environment doesn't provide authentication information, we should have a default generic identity of anonymous slapped on that.







Sent: 12 July 2011 16:46
To: open...@googlegroups.com
Subject: Re : RE: Re : Re: Re : RE: Re : RE: Re : RE: [openrasta] Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

mark Kharitonov

unread,
Jul 13, 2011, 10:53:16 AM7/13/11
to open...@googlegroups.com
Hi Seb.

I am sorry for misleading you.

I have found the problem.

First of all, the User was not null, it was just that context.User.Identity.IsAuthenticated was false. My mistake - please close the issue.

I have also found why it was not authenticated.

My original code was like this:


  public class NegotiateAuthenticationScheme : IAuthenticationScheme
  {
    private readonly IUserRolesProvider m_userRolesProvider;
    private readonly IMapPrincipalToUser m_mapPrincipalToUser;
    private readonly ICommunicationContext m_context;

    const string Scheme = "Negotiate";

    public NegotiateAuthenticationScheme(IUserRolesProvider userRolesProvider, IMapPrincipalToUser mapPrincipalToUser, ICommunicationContext context)

    {
      m_userRolesProvider = userRolesProvider;
      m_mapPrincipalToUser = mapPrincipalToUser;
      m_context = context;

    }

    public string Name    {      get { return Scheme; }    }

    public AuthenticationResult Authenticate(IRequest request)
    {
      if (m_context.User.Identity.AuthenticationType == Scheme)
      {
        var user = m_mapPrincipalToUser.GetUserName(m_context.User);

        return new AuthenticationResult.Success(user, m_userRolesProvider.GetUserRoles(user));
      }

      return new AuthenticationResult.Failed();
    }

    public void Challenge(IResponse response)    {    }
  }

And here is the flow that makes the code fail:

  1. An AspNetCommunicationContext instance is created - User.Identity.IsAuthenticated == false.
  2. RequiresAuthenticationInterceptor stops execution, because authentication is needed.
  3. A NegotiateAuthenticationScheme instance is created (in order to add its challenge), it is passed the AspNetCommunicationContext instance created in (1).
  4. A new AspNetCommunicationContext instance is created - User.Identity.IsAuthenticated == true.
  5. NegotiateAuthenticationScheme.Authenticate is called on the same NegotiateAuthenticationScheme instance created in (3).
  6. The NegotiateAuthenticationScheme instance references the stale AspNetCommunicationContext instance, where the user is still unauthenticated.
The root of the problem is that the authentication scheme is a true singleton, while the communication context is not.

Hence, the fix is simple - either register the authentication schemes as transient or do not depend on the communication context in the constructor, because it is in effect transient in this partcular well ... context - using the dependency resolver to get it when needed works fine as well.

Again, I am sorry for the fuss.

I would like, though, to know what you think about my user management - you had a reservation and I replied.

Thanks.

Sebastien Lambla

unread,
Jul 13, 2011, 12:55:01 PM7/13/11
to open...@googlegroups.com
 That's fine, good to know you find out where it came from. Sure we all learnt a few interesting things in there :)

The auth scheme should definitely be a transient, we ought to make sure this is there indeed.

As for the user management, if you do have to map a windows account to your app account, then yes your solution seems mighty fine, I was just putting a reservation on how generic that solution could be outside of that specific problem :)

Sent: 13 July 2011 15:53
To: open...@googlegroups.com
Subject: Re : RE: Re : RE: Re : Re: Re : RE: Re : RE: Re : RE: [openrasta] Accessing a resource with basic authentication when hosted in IIS 7 from Internet Explorer (9) results in an infinite recursion - please help.

mark Kharitonov

unread,
Jul 28, 2011, 5:21:40 AM7/28/11
to open...@googlegroups.com
I have written and tested the pipeline contributor to replace NegotiateAuthenticationScheme.
Here is the code for those wishing to use it:

using System.Security.Principal;
using MyRestServer.Auth;
using OpenRasta.Pipeline;
using OpenRasta.Web;

namespace MyRestServer.Pipeline.Contributors
{
  public class OverrideNegotiatedPrincipalContributor : IPipelineContributor
  {
    private const string SchemeName = "Negotiate";

    private readonly IUserRolesProvider m_userRolesProvider;
    private readonly IMapPrincipalToUser m_mapPrincipalToUser;

    public OverrideNegotiatedPrincipalContributor(IUserRolesProvider userRolesProvider, IMapPrincipalToUser mapPrincipalToUser)

    {
      m_userRolesProvider = userRolesProvider;
      m_mapPrincipalToUser = mapPrincipalToUser;
    }

    #region Implementation of IPipelineContributor

    public void Initialize(IPipeline pipeline)
    {
      pipeline.Notify(Override).Before<KnownStages.IAuthentication>();
    }

    #endregion

    private PipelineContinuation Override(ICommunicationContext context)
    {
      if (context.User.Identity.AuthenticationType == SchemeName)

      {
        // The user has already been authenticated by the web server using the
        // window authentication. OR did not have a say in it, because both the browser and the web
        // server did it "behind its back". How come? Observe:
        // - Browser is IE
        // - Web server is IIS
        // - OR added WWW-Authenticate: Basic
        // - IIS added WWW-Authenticate: Negotiate
        // - IIS added WWW-Authenticate: NTLM
        // - IE decided to ignore the basic authentication and went on the Negotiate, because it has the
        //   "Use Windows Authentication" security option enabled. In our case this is NTLM.
        // - IIS successfully authenticates the user using windows authentication and passes the request
        //   to OR
        // - OR gets the request with Authorization: Negotiate, rather than Authorization: Basic
        // - At this point the user has been successfully authenticated, but not with the basic authentication
        var requestedAuthSchemeName = ExtractRequestedAuthScheme(context.Request);
        if (requestedAuthSchemeName == SchemeName)
        {
          context.Request.Headers.Remove("Authorization");
          var user = m_mapPrincipalToUser.GetUserName(context.User);
          var identity = new GenericIdentity(user, SchemeName);
          context.User = new GenericPrincipal(identity, m_userRolesProvider.GetUserRoles(user));
        }
      }

      return PipelineContinuation.Continue;
    }

    private static string ExtractRequestedAuthScheme(IHttpMessage request)
    {
      var authRequestHeader = request.Headers["Authorization"];

      if (string.IsNullOrEmpty(authRequestHeader))
      {
        return null;
      }

      var requestedAuthSchemeName = authRequestHeader.Split(' ')[0];

      return string.IsNullOrEmpty(requestedAuthSchemeName) ? null : requestedAuthSchemeName;
    }
  }
}

Reply all
Reply to author
Forward
0 new messages