Expand description
§authentic
Handle authentication of HTTP calls.
Authentication protocols can require specific workflows, such as making third-party calls to refresh a token or performing an initial request to get challenge information.
Using a fixed code structure, authentic
can perform the necessary interactions for each authentication protocol. This allows protocols to be changed easily.
For example, the following code uses reqwest
to access a site using HTTP Basic authentication. (See the repository tests directory for fully working examples).
// One-time code:
let client = reqwest::blocking::Client::new();
let mut realm_credentials = HashMap::new();
realm_credentials.insert(
"Fake Realm".into(),
Arc::new(UsernamePasswordCredential::new("username", "password")),
);
let credential = Arc::new(HttpRealmCredentials::new(realm_credentials));
// Per-request code:
let mut authentication = HttpAuthentication::new(credential.clone());
let response = loop {
while let Some(auth_step) = authentication.step()? {
match auth_step {
AuthenticationStep::Request(request) => {
let auth_response = client.execute(request);
authentication.respond(auth_response);
}
AuthenticationStep::WaitFor(duration) => {
std::thread::sleep(duration);
}
}
}
let response = client
.get("https://httpbin.org/basic-auth/username/password")
.with_authentication(&authentication)?
.send()?;
if authentication.has_completed(&response)? {
break response;
}
};
The creation of the request takes place inside a loop. First, the authentication protocol is given an opportunity to perform any third-party calls using step()
.
HTTP Basic authentication does not use this, but it can be used, for example, to refresh an expired OAuth2 access token.
The request is created using a standard reqwest::RequestBuilder
, using a new with_authentication()
method to modify the request for the authentication protocol.
For HTTP authentication, the first iteration makes no change to the request.
The request is sent and a response is received. For HTTP authentication, this returns a 401 Unauthorized
response.
The has_completed()
method checks if the response is ready to be returned or if the authentication protocol needs to retry.
For HTTP authentication, this reads the returned www-authenticate
challenge and establishes the correct credentials.
As the request needs to be retried, has_completed()
returns false
and a second iteration begins.
On the second iteration of the loop, with_authentication()
adds the credentials as the Authorization
header to the request. The request is authenticated and the response contains the correct data. has_completed()
will return true
and the loop exits with the response.
§HTTP library features
authentic
supports asynchronous code using hyper
or reqwest
, and blocking code using reqwest
.
Specify the library using the following features:
hyper-client
reqwest-async
reqwest-blocking
§Algorithm features
The above per-request code works for all supported authentication methods, but requires both the step
and loop
features to be enabled.
If the loop
feature is not enabled, then the outer loop is not required.
If the step
feature is not enabled, then the inner while
loop is not required.
For example, a JWT credential requires the step
feature, but does not require the loop
feature.
Hence the code can remove the outer loop.
(Note that the JWT credential also requires the jwt
feature.)
// One-time code:
let client = reqwest::blocking::Client::new();
let credential = Arc::new(JsonWebTokenCredential::new(
jsonwebtoken::Header::new(jsonwebtoken::Algorithm::RS256),
jsonwebtoken::EncodingKey::from_rsa_pem(private_key)?,
Duration::from_secs(10 * 60),
));
// Per-request code:
let mut authentication = BearerAuthentication::new(credential.clone());
while let Some(auth_step) = authentication.step()? {
match auth_step {
AuthenticationStep::Request(request) => {
let auth_response = client.execute(request);
authentication.respond(auth_response);
}
AuthenticationStep::WaitFor(duration) => {
std::thread::sleep(duration);
}
}
}
let response = client
.get(url)
.with_authentication(&authentication)?
.send()?;
If an API always requires basic authentication with specific credentials, neither the step
or loop
features are required:
// One-time code:
let client = reqwest::blocking::Client::new();
let credential = Arc::new(UsernamePasswordCredential::new("username", "password"));
// Per-request code:
let mut authentication = BasicAuthentication::new(credential.clone());
let response = client
.get("https://httpbin.org/basic-auth/username/password")
.with_authentication(&authentication)?
.send()?;
§Supported combinations
The supported algorithm-credential pairs, and the features required to enable them, are:
NoAuthentication
BasicAuthentication<UsernamePasswordCredential>
BearerAuthentication<JsonWebTokenCredential>
(features = ["jwt", "step"]
)BearerAuthentication<TokenCredential>
HeaderAuthentication<JsonWebTokenCredential>
(features = ["jwt", "step"]
)HeaderAuthentication<TokenCredential>
HttpAuthentication<HttpRealmCredentials<UsernamePasswordCredential>>
(features = ["loop"]
)
Modules§
- credential
- hyper
- Authentication protocols for use with
hyper
. Use thehyper-async
feature to enable these. - reqwest
- Authentication protocols for use with
reqwest
. Use thereqwest-async
feature to enable these.