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.
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.
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.
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.
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.