HTTP authentication challenges

A non-empty challenges container in the response indicates an authentication challenge from the server. This container's type is x::http::useragent::base::challenges_t. It's a std::map, keyed by the authentication realm. The following example displays the document returned by an HTTP GET request. An authentication challenge from the server prompts for a userid and a password, then the request gets repeated.

void showuri(const x::http::useragent &ua, const x::uriimpl &uri)
{
	bool has_challenge;

	do
	{
		auto resp=ua->request(x::http::GET, uri);

		std::copy(resp->begin(), resp->end(),
			  std::ostreambuf_iterator<char>(std::cout));

                std::cout << std::flush;
		has_challenge=false;

		for (const auto &challenge:resp->challenges)
		{
			has_challenge=true;

			std::cout << x::http::auth_tostring(challenge.second->scheme)
			          << " authentication required for "
				  << challenge.first << ":" << std::endl
				  << "Userid: " << std::flush;

			std::string userid, password;

			if (std::getline(std::cin, userid).eof())
				return;

			std::cout << "Password: " << std::flush;

			if (std::getline(std::cin, password).eof())
				return;

			ua->set_authorization(resp, challenge.second,
					      userid,
					      password);
		}
	} while (has_challenge);
}

RFC 2617 allows an HTTP server or a proxy to return multiple challenges for different authentication realms, but in practice there will usually be only one. The challenges member in the response object is a container whose type is x::http::useragent::base::challenges_t. This is a typedef for a std::map, keyed by the authentication realm (but, as just mentioned, it will nearly always have a single challenge). set_authorization() takes the following parameters: the server's response, the value in the challenges container, and the authorization credentials. This takes effect for the following requests that use the same user agent object, and this example simply tries again.

The value in the challenges map is an opaque reference-counted object that gets passed to set_authorization(). It specifies the challenge for which the given credentials apply. The object's scheme has the following values:

x::http::auth::basic

The basic authorization scheme that sends the userid and the password to the server, without encryption.

x::http::auth::digest

A more secure digest authorization scheme. The application must link with -lcxxtls to enable support for digest authorization, and invoke x::http::useragent::base::https_enable() (only required for static linking, ordinary dynamic linking with -lcxxtls is sufficient).

The selection of the authorization scheme depends on the server's capabilities. The digest authorization scheme gets selected if the server supports both the basic and the digest authorization. An application can check the authorization scheme, and refuse to use the basic authorization scheme with a non-https URL.

Note

The user agent object maintains an internal authentication cache, and automatically responds to additional challenges from the same authentication realm. If the server rejects the authorization and reissues a challenge, the cached authentication gets removed, and the user agent returns the response object with the new challenge.

Note

A server's authentication challenge is an ordinary HTTP response, with a content document, that should be read and processed. The original response object should then get destroyed, before trying again. The above example always shows the contents of the server's response, including server challenges.

A server response object must go out of scope and get destroyed before the user agent object can reuse its persistent HTTP connection, with the same server or proxy. Otherwise, the user agent object has to create another connection. The above example intentionally structures its processing loop so that the original response object gets discarded, before looping around and creating another response object.

The userid and the password for basic authorization can also be given directly in an https URI:

auto resp=ua->request(x::http::GET, "https://user:password@example.com/");

When the authentication requirement is known in advance, giving the credentials in the URI allows the inclusion of this authorization in the initial request, saving a formal challenge. Although the user agent object does not have the authentication realm, the authorization credentials are blindly sent in the request, and the user agent object caches them as the default authorization for all future requests to the same server. This works with basic authorization scheme only. Digest authorization responses require receiving a challenge, first, to obtain the realm and the nonce key.

Note

Specifying the userid and the password in an unencrypted http URI also works. However, explicit userids and passwords are sent as a basic, unhashed, authorization requests, and without https the userid and the password gets sent completely unencrypted. This is advisable only for internal applications, and should not be used on public networks.

Note

Authorizations given in the URI get specified for the origin server authentication only. The only way to set intermediate proxy authorization is by responding to its authentication challenge, in the response object.