{"action":"created","issue":{"url":"https://api.github.com/repos/eclipse/jetty.project/issues/147","repository_url":"https://api.github.com/repos/eclipse/jetty.project","labels_url":"https://api.github.com/repos/eclipse/jetty.project/issues/147/labels{/name}","comments_url":"https://api.github.com/repos/eclipse/jetty.project/issues/147/comments","events_url":"https://api.github.com/repos/eclipse/jetty.project/issues/147/events","html_url":"https://github.com/eclipse/jetty.project/issues/147","id":134126016,"number":147,"title":"Jetty Client should allow preemptive basic auth to be completely disabled","user":{"login":"jmcc0nn3ll","id":273327,"avatar_url":"https://avatars.githubusercontent.com/u/273327?v=3","gravatar_id":"","url":"https://api.github.com/users/jmcc0nn3ll","html_url":"https://github.com/jmcc0nn3ll","followers_url":"https://api.github.com/users/jmcc0nn3ll/followers","following_url":"https://api.github.com/users/jmcc0nn3ll/following{/other_user}","gists_url":"https://api.github.com/users/jmcc0nn3ll/gists{/gist_id}","starred_url":"https://api.github.com/users/jmcc0nn3ll/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jmcc0nn3ll/subscriptions","organizations_url":"https://api.github.com/users/jmcc0nn3ll/orgs","repos_url":"https://api.github.com/users/jmcc0nn3ll/repos","events_url":"https://api.github.com/users/jmcc0nn3ll/events{/privacy}","received_events_url":"https://api.github.com/users/jmcc0nn3ll/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/eclipse/jetty.project/labels/Bug","name":"Bug","color":"eb6420"}],"state":"closed","locked":false,"assignee":{"login":"sbordet","id":414681,"avatar_url":"https://avatars.githubusercontent.com/u/414681?v=3","gravatar_id":"","url":"https://api.github.com/users/sbordet","html_url":"https://github.com/sbordet","followers_url":"https://api.github.com/users/sbordet/followers","following_url":"https://api.github.com/users/sbordet/following{/other_user}","gists_url":"https://api.github.com/users/sbordet/gists{/gist_id}","starred_url":"https://api.github.com/users/sbordet/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/sbordet/subscriptions","organizations_url":"https://api.github.com/users/sbordet/orgs","repos_url":"https://api.github.com/users/sbordet/repos","events_url":"https://api.github.com/users/sbordet/events{/privacy}","received_events_url":"https://api.github.com/users/sbordet/received_events","type":"User","site_admin":false},"milestone":null,"comments":5,"created_at":"2016-02-16T23:26:00Z","updated_at":"2016-04-05T05:51:39Z","closed_at":"2016-04-01T13:21:53Z","body":"migrated from Bugzilla [#432943](https://bugs.eclipse.org/bugs/show_bug.cgi?id=432943)\nstatus ASSIGNED severity *normal* in component *client* for *9.1.x*\nReported in version *unspecified* on platform *PC*\nAssigned to: Simone Bordet\n\nOn 2014-04-16 11:28:25 -0400, Mark Mielke wrote:\n\n> By default, Jetty Client will try the request without basic auth. Then, upon authorization failure, it will try the request with basic auth. If basic auth succeeds, it \"remembers\" this for future via an AuthenticationResult that gets cached.\n> \n> For my scenario, which is actually \"login to a JIRA backed by LDAP authentication\", when I issue HTTP basic auth requests with username and password, the requests complete in around 250 milliseconds. Once authenticated, the user's session (identified using a cookie), the request can complete in 7 milliseconds or less.\n> \n> When I use Jetty Client to authenticate, all requests are completing in the 250 millisecond range. This is because all future requests continue to include the username and password.\n> \n> I added a listener using httpClient.getRequestListeners.add() that has \"onQueued\" defined as:\n> \n> httpClient.getAuthenticationStore().clearAuthenticationResults();\n> \n> This forces the authentication results cache to always be reset before every request. After this, only the first request takes 250 milliseconds, and all future requests (until the cookie expires) complete in 7 milliseconds or less.\n> \n> This also has the added \"security\" feature that the password is not being sent with every request.\n> \n> I don't know how much of a \"special case\" this scenario is. I think Apache HttpClient may behave the same way. I'm trying to upgrade from Jersey Client 1.x / Apache HttpClient 2.2.x and my custom Jersey 1.x method of authentication handling is now broken. Getting Jersey Client 2.x to work \"properly\" has been painful. I finally threw out Jersey and went direct to Jetty Client as I have enjoyed working with your product so far.\n> \n> The above work-around \"works\". I may go production with it. But, I'm concerned about the long-term behaviour of the function, and the inherent race condition. That is, there is a race between when the Jetty Client authentication protocol handler stores the authentication results, and where the onQueued method clears it, so any concurrent queries run at the same time may re-use the authentication results. Also, I would like to know that the behaviour of the authenticator won't change in a future release that would cause the above work-around to fail. If there was a specific feature to provide this capability, then I wouldn't have to be concerned about either of these?\n> \n> In terms of implementation, I think I am asking for a documented interface to disable the newRequest.onResponseSuccess() code described below:\n> \n> AuthenticationProtocolHandler.AuthenticationListener.onComplete():\n> \n>             newRequest.onResponseSuccess(new Response.SuccessListener()\n>             {\n>                 @Override\n>                 public void onSuccess(Response response)\n>                 {\n>                     client.getAuthenticationStore().addAuthenticationResult(authnResult);\n>                 }\n>             }).send(null);\n> \n> Thanks!\n\n"},"comment":{"url":"https://api.github.com/repos/eclipse/jetty.project/issues/comments/205657433","html_url":"https://github.com/eclipse/jetty.project/issues/147#issuecomment-205657433","issue_url":"https://api.github.com/repos/eclipse/jetty.project/issues/147","id":205657433,"user":{"login":"MarkMielke","id":1708998,"avatar_url":"https://avatars.githubusercontent.com/u/1708998?v=3","gravatar_id":"","url":"https://api.github.com/users/MarkMielke","html_url":"https://github.com/MarkMielke","followers_url":"https://api.github.com/users/MarkMielke/followers","following_url":"https://api.github.com/users/MarkMielke/following{/other_user}","gists_url":"https://api.github.com/users/MarkMielke/gists{/gist_id}","starred_url":"https://api.github.com/users/MarkMielke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/MarkMielke/subscriptions","organizations_url":"https://api.github.com/users/MarkMielke/orgs","repos_url":"https://api.github.com/users/MarkMielke/repos","events_url":"https://api.github.com/users/MarkMielke/events{/privacy}","received_events_url":"https://api.github.com/users/MarkMielke/received_events","type":"User","site_admin":false},"created_at":"2016-04-05T05:51:39Z","updated_at":"2016-04-05T05:51:39Z","body":"@sbordet I think just over thinking it... :-)\r\n\r\nPreemptive authentication is a problem, because it sends the username + password with EVERY request. Unless the server implements sort of caching to detect \"hey, the user sent the same basic authentication 2 seconds ago, and I approved it then, so let's automatically approve it now\", then it needs to re-authenticate the username + password with EVERY request. This is what costs. Authentication locally is usually expensive (processing power), but fast. Authentication with an external service such as LDAP can be much more expensive, as it may require first lookup of the user (LDAP bind + search) and then login (LDAP bind).\r\n\r\nIf we don't use preemptive authentication, then there are these four states:\r\n\r\n1) Initially, the request is not authorized at all. No basic authentication. No cookie. The server returns HTTP 401 Unauthorized. The client retries with basic authorization specifying the username and password. The server authorizes the request and returns the response with a cookie.\r\n\r\n2) For a period of time, the cookie can now be used to authenticate the request. The cookie ends up being a very fast hash lookup to identify the session data, which includes recognition of the autohrized user. Requests in these situations are authorized and processed quickly.\r\n\r\n3) After a period of time, the cookie expires on the server. The session data is ignored or forgotten. A new session is generated. In this case, HTTP 401 Unauthorized would be returned, and the client needs to respond with basic authentication. The session is renewed. The server autohrizes the request and returns the response with the same or a new cookie.\r\n\r\n4) After a different period of time, the client recognizes that the cookie has expired and stops sending it. This situation then becomes the same as state #1.\r\n\r\nI think this sort of issue happens a lot more than most people realize. It is just that very often, people don't understand why their requests are slow, and they don't realize it is due to authentication overhead. They likely attribute the slow performance with an assumption that the request processing is slow, rather than the request authorization.\r\n\r\nI know some servers do cache basic authentication to work around this. I know that JIRA does not. If I specify the username + password, it checks it. (Although there has been JIRA versions since I raised this, and it is possible it is \"fixed\" now with caching... I would have to test to see...)\r\n"}}