Implementation:ClickHouse ClickHouse Poco OAuth10Credentials
base/poco/Net/src/OAuth10Credentials.cpp:1-365
ClickHouse_ClickHouse
ClickHouse_ClickHouse_HTTP_Authentication
Purpose
Implements the `Poco::Net::OAuth10Credentials` class, which provides OAuth 1.0 (RFC 5849) authentication for HTTP requests. The class supports both PLAINTEXT and HMAC-SHA1 signature methods, can sign outgoing requests and verify incoming ones, and constructs the `Authorization` header with proper OAuth parameter encoding.
Code Reference
HMAC-SHA1 Signature Creation
The `createSignature` method builds the signature base string and computes the HMAC-SHA1 digest:
std::string OAuth10Credentials::createSignature(
const HTTPRequest& request, const std::string& uri,
const HTMLForm& params, const std::string& nonce,
const std::string& timestamp) const
{
std::map<std::string, std::string> paramsMap;
paramsMap["oauth_version"] = "1.0";
paramsMap["oauth_consumer_key"] = percentEncode(_consumerKey);
paramsMap["oauth_nonce"] = percentEncode(nonce);
paramsMap["oauth_signature_method"] = "HMAC-SHA1";
paramsMap["oauth_timestamp"] = timestamp;
if (!_token.empty())
paramsMap["oauth_token"] = percentEncode(_token);
if (!_callback.empty())
paramsMap["oauth_callback"] = percentEncode(_callback);
for (auto it = params.begin(); it != params.end(); ++it)
paramsMap[percentEncode(it->first)] = percentEncode(it->second);
// Build sorted parameter string: key=value&key=value&...
std::string paramsString;
for (auto it = paramsMap.begin(); it != paramsMap.end(); ++it)
{
if (it != paramsMap.begin()) paramsString += '&';
paramsString += it->first;
paramsString += "=";
paramsString += it->second;
}
// Signature base = METHOD&percentEncode(URI)&percentEncode(params)
std::string signatureBase = request.getMethod();
signatureBase += '&';
signatureBase += percentEncode(uri);
signatureBase += '&';
signatureBase += percentEncode(paramsString);
// Signing key = percentEncode(consumerSecret)&percentEncode(tokenSecret)
std::string signingKey;
signingKey += percentEncode(_consumerSecret);
signingKey += '&';
signingKey += percentEncode(_tokenSecret);
Poco::HMACEngine<Poco::SHA1Engine> hmacEngine(signingKey);
hmacEngine.update(signatureBase);
Poco::DigestEngine::Digest digest = hmacEngine.digest();
// Base64-encode the digest
std::ostringstream digestBase64;
Poco::Base64Encoder base64Encoder(digestBase64);
base64Encoder.write(reinterpret_cast<char*>(&digest[0]), digest.size());
base64Encoder.close();
return digestBase64.str();
}
PLAINTEXT Signing
void OAuth10Credentials::signPlaintext(HTTPRequest& request) const
{
std::string signature(percentEncode(_consumerSecret));
signature += '&';
signature += percentEncode(_tokenSecret);
std::string authorization(SCHEME);
// Append realm, consumer_key, signature, token, callback, version
request.set(HTTPRequest::AUTHORIZATION, authorization);
}
Request Verification
bool OAuth10Credentials::verify(const HTTPRequest& request,
const URI& uri, const HTMLForm& params)
{
// Extract OAuth parameters from Authorization header
// Recompute signature using extracted nonce and timestamp
// Compare computed signature with provided signature
return refSignature == signature;
}
Nonce Generation
std::string OAuth10Credentials::createNonce() const
{
std::ostringstream base64Nonce;
Poco::Base64Encoder base64Encoder(base64Nonce);
Poco::RandomInputStream randomStream;
for (int i = 0; i < 32; i++)
{
base64Encoder.put(randomStream.get());
}
base64Encoder.close();
std::string nonce = base64Nonce.str();
return Poco::translate(nonce, "+/=", "");
}
Credential Extraction from Request
OAuth10Credentials::OAuth10Credentials(const HTTPRequest& request)
{
if (request.hasCredentials())
{
std::string authScheme;
std::string authParams;
request.getCredentials(authScheme, authParams);
if (icompare(authScheme, SCHEME) == 0)
{
HTTPAuthenticationParams params(authParams);
std::string consumerKey = params.get("oauth_consumer_key", "");
URI::decode(consumerKey, _consumerKey);
std::string token = params.get("oauth_token", "");
URI::decode(token, _token);
std::string callback = params.get("oauth_callback", "");
URI::decode(callback, _callback);
}
else throw NotAuthenticatedException("No OAuth credentials in Authorization header");
}
else throw NotAuthenticatedException("No Authorization header found");
}
I/O Contract
| Input | Output | Side Effects |
|---|---|---|
| Consumer key + secret, token + secret | `OAuth10Credentials` object | None |
| `HTTPRequest&` + `URI` via `authenticate` (HMAC-SHA1) | None | Sets `Authorization` header with OAuth params and HMAC-SHA1 signature |
| `HTTPRequest&` via `authenticate` (PLAINTEXT) | None | Sets `Authorization` header with plaintext signature |
| `HTTPRequest&` + `URI` via `verify` | `bool` (signature valid) | Extracts and decodes OAuth params from request; populates `_consumerKey`, `_token`, `_callback` |
| `HTTPRequest&` (constructor) | `OAuth10Credentials` with extracted key/token/callback | Throws `NotAuthenticatedException` if no OAuth credentials found |
| `createNonce` | Random 32-byte Base64 string (without `+/=` characters) | Reads from `Poco::RandomInputStream` |
Usage Examples
// Signing a request with HMAC-SHA1
Poco::Net::OAuth10Credentials creds("consumerKey", "consumerSecret",
"accessToken", "tokenSecret");
Poco::Net::HTTPRequest request(Poco::Net::HTTPRequest::HTTP_GET, "/resource");
Poco::URI uri("https://api.example.com/resource");
creds.authenticate(request, uri, Poco::Net::OAuth10Credentials::SIGN_HMACSHA1);
// request now has Authorization header with OAuth signature
// Verifying an incoming request
Poco::Net::OAuth10Credentials serverCreds;
serverCreds.setConsumerSecret("consumerSecret");
serverCreds.setTokenSecret("tokenSecret");
bool valid = serverCreds.verify(incomingRequest, requestUri);
// Using PLAINTEXT signature
creds.authenticate(request, uri, Poco::Net::OAuth10Credentials::SIGN_PLAINTEXT);
Internal Details
- The `percentEncode` method delegates to `Poco::URI::encode` with a custom set of reserved characters: `!?#/'\",;:$&()[]*+=@`.
- Parameters are sorted lexicographically via `std::map` before building the signature base string, as required by the OAuth 1.0 specification.
- The `authenticate` method strips the query and fragment from the URI before signing, as the OAuth spec requires a base string URI without query components.
- The `nonceAndTimestampForTesting` method allows injection of fixed nonce and timestamp values for deterministic testing.
- The `verify` method supports both PLAINTEXT and HMAC-SHA1 methods. It recomputes the signature using the provided nonce and timestamp and compares it against the signature from the `Authorization` header.
- Nonces are generated from 32 bytes of `Poco::RandomInputStream` data, Base64-encoded, with `+`, `/`, and `=` characters stripped to produce URL-safe values.
- The class reads/writes the `Authorization` header using the `OAuth` scheme prefix, with parameters formatted as `key="value"` pairs separated by commas.
- Version is always set to `"1.0"`.