8: Authentication

Users can log into HTTP sites via various mechanisms including:

  • Basic authentication: the server challenges the client, the web browser may prompt the user for username & password and sends them with subsequent requests. The credentials are not protected from snoopers.
  • Digest authentication: the server challenges the client, the web browser may prompt the user for username & password and sends them with subsequent requests. The credentials are protected from snoopers.
  • Certificate authentication: the server challenges the client for a certificate and the client sends it, if it can. This is discussed on the previous page.
  • Form authentication: the website includes a form for username & password, and then a cookie token is used. This topic is beyond the scope of this tutorial, but is supported via cookies.
  • OpenID or OAuth: a separate OpenID/OAuth service validates the username, password etc, and then some cookies containing OpenID or OAuth tokens are used. This topic is beyond the scope of this tutorial, but is supported via cookies and other headers as needed.

Bee-client supports basic and digest authentication directly, and the others indirectly. NTLM and KerberosV5 are not supported at present, although the API allows you to send the necessary headers if you know them.

Authentication always demands that the traffic is encrypted using HTTPS. Nowadays, it is hard to justify not doing so, even for private LAN traffic.

Basic Authentication

* Hand-Implemented Case: Just Send The Authentication Header

When a server is protected by basic authentication, any simple attempt to access protected URLs will be rejected with a 401 status (authentication required). This example illustrates this:

...  imports
object Example6a1 extends App {
  val url = "http://beeclient/private/lorem2.txt"
  val httpClient = new HttpClient
  val response401 = httpClient.get(url) // no authentication
  println(response401.status)
  println(response401.headers(HeaderName.WWW_AUTHENTICATE).toAuthenticateValue)
}

Clearly, a rejection is unsatisfactory. If we know a correct username and password, we can supply them via a Credential instance:

...  imports
object Example6a2 extends App {
  val url = "http://beeclient/private/lorem2.txt"
  val httpClient = new HttpClient
  val credential = new Credential("bigbee", "HelloWorld")
  val login = Headers(credential.toBasicAuthHeader)
  val response200 = httpClient.get(url, login) // with authentication
  println(response200.status)
  println(response200.body)
}

The server confirms that the correct authentication was sent and responds with 200 and the normal content.

* Automatic Case: Use HttpBrowser with an AuthenticationRegistry

HttpClient and HttpBrowser both implement the Http client trait. HttpClient is intended for normal simple cases. HttpBrowser augments it with the ability to aggregate cookies and authentication state automatically. Although HttpBrowser contains mutable state, it is safe to share instances between more than one thread.

For the purposes of authentication, you need to construct a CredentialSuite and pass it to the HttpBrowser. CredentialSuite contains the mappings between authentication realms and their credentials, so you construct it with a Map of realm names to Credential instances like this:

...  imports
object Example6b extends App {
  val url = "http://beeclient/private/lorem2.txt"
  val credential = new Credential("bigbee", "HelloWorld")
  val suite = new CredentialSuite(Map("Restricted" -> credential))
  val browser = new HttpBrowser(credentialSuite = suite)
  val response200 = browser.get(url)
  println(response200.status)
  println(response200.body)
}

On the first occasion, the HttpBrowser will internally make two requests: the first one returns a 401 and the name of a realm. Given that the CredentialSuite maps that realm to the necessary username/password combination, a second attempt is made on the same request, this time with the authentication header. The result is a 200 and the expected response body.

The HttpBrowser caches the URL-to-realm association so that subsequent requests to the same URL tree do not need to suffer the double round trip, which is good news for network latency.

Digest Authentication

(in preparation)