// // OAuth10Credentials.cpp // // Library: Net // Package: OAuth // Module: OAuth10Credentials // // Copyright (c) 2014, Applied Informatics Software Engineering GmbH. // and Contributors. // // SPDX-License-Identifier: BSL-1.0 // #include "Poco/Net/OAuth10Credentials.h" #include "Poco/Net/HTTPRequest.h" #include "Poco/Net/HTMLForm.h" #include "Poco/Net/NetException.h" #include "Poco/Net/HTTPAuthenticationParams.h" #include "Poco/SHA1Engine.h" #include "Poco/HMACEngine.h" #include "Poco/Base64Encoder.h" #include "Poco/RandomStream.h" #include "Poco/Timestamp.h" #include "Poco/NumberParser.h" #include "Poco/NumberFormatter.h" #include "Poco/Format.h" #include "Poco/String.h" #include #include namespace Poco { namespace Net { const std::string OAuth10Credentials::SCHEME = "OAuth"; OAuth10Credentials::OAuth10Credentials() { } OAuth10Credentials::OAuth10Credentials(const std::string& consumerKey, const std::string& consumerSecret): _consumerKey(consumerKey), _consumerSecret(consumerSecret) { } OAuth10Credentials::OAuth10Credentials(const std::string& consumerKey, const std::string& consumerSecret, const std::string& token, const std::string& tokenSecret): _consumerKey(consumerKey), _consumerSecret(consumerSecret), _token(token), _tokenSecret(tokenSecret) { } OAuth10Credentials::OAuth10Credentials(const Poco::Net::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", authScheme); } else throw NotAuthenticatedException("No Authorization header found"); } OAuth10Credentials::~OAuth10Credentials() { } void OAuth10Credentials::setConsumerKey(const std::string& consumerKey) { _consumerKey = consumerKey; } void OAuth10Credentials::setConsumerSecret(const std::string& consumerSecret) { _consumerSecret = consumerSecret; } void OAuth10Credentials::setToken(const std::string& token) { _token = token; } void OAuth10Credentials::setTokenSecret(const std::string& tokenSecret) { _tokenSecret = tokenSecret; } void OAuth10Credentials::setRealm(const std::string& realm) { _realm = realm; } void OAuth10Credentials::setCallback(const std::string& callback) { _callback = callback; } void OAuth10Credentials::authenticate(HTTPRequest& request, const Poco::URI& uri, SignatureMethod method) { HTMLForm emptyParams; authenticate(request, uri, emptyParams, method); } void OAuth10Credentials::authenticate(HTTPRequest& request, const Poco::URI& uri, const Poco::Net::HTMLForm& params, SignatureMethod method) { if (method == SIGN_PLAINTEXT) { signPlaintext(request); } else { URI uriWithoutQuery(uri); uriWithoutQuery.setQuery(""); uriWithoutQuery.setFragment(""); signHMACSHA1(request, uriWithoutQuery.toString(), params); } } bool OAuth10Credentials::verify(const HTTPRequest& request, const Poco::URI& uri) { HTMLForm params; return verify(request, uri, params); } bool OAuth10Credentials::verify(const HTTPRequest& request, const Poco::URI& uri, const Poco::Net::HTMLForm& params) { if (request.hasCredentials()) { std::string authScheme; std::string authParams; request.getCredentials(authScheme, authParams); if (icompare(authScheme, SCHEME) == 0) { HTTPAuthenticationParams oauthParams(authParams); std::string version = oauthParams.get("oauth_version", "1.0"); if (version != "1.0") throw NotAuthenticatedException("Unsupported OAuth version", version); _consumerKey.clear(); std::string consumerKey = oauthParams.get("oauth_consumer_key", ""); URI::decode(consumerKey, _consumerKey); _token.clear(); std::string token = oauthParams.get("oauth_token", ""); URI::decode(token, _token); _callback.clear(); std::string callback = oauthParams.get("oauth_callback", ""); URI::decode(callback, _callback); std::string nonceEnc = oauthParams.get("oauth_nonce", ""); std::string nonce; URI::decode(nonceEnc, nonce); std::string timestamp = oauthParams.get("oauth_timestamp", ""); std::string method = oauthParams.get("oauth_signature_method", ""); std::string signatureEnc = oauthParams.get("oauth_signature", ""); std::string signature; URI::decode(signatureEnc, signature); std::string refSignature; if (icompare(method, "PLAINTEXT") == 0) { refSignature = percentEncode(_consumerSecret); refSignature += '&'; refSignature += percentEncode(_tokenSecret); } else if (icompare(method, "HMAC-SHA1") == 0) { URI uriWithoutQuery(uri); uriWithoutQuery.setQuery(""); uriWithoutQuery.setFragment(""); refSignature = createSignature(request, uriWithoutQuery.toString(), params, nonce, timestamp); } else throw NotAuthenticatedException("Unsupported OAuth signature method", method); return refSignature == signature; } else throw NotAuthenticatedException("No OAuth credentials found in Authorization header"); } else throw NotAuthenticatedException("No Authorization header found"); } void OAuth10Credentials::nonceAndTimestampForTesting(const std::string& nonce, const std::string& timestamp) { _nonce = nonce; _timestamp = timestamp; } void OAuth10Credentials::signPlaintext(Poco::Net::HTTPRequest& request) const { std::string signature(percentEncode(_consumerSecret)); signature += '&'; signature += percentEncode(_tokenSecret); std::string authorization(SCHEME); if (!_realm.empty()) { Poco::format(authorization, " realm=\"%s\",", _realm); } Poco::format(authorization, " oauth_consumer_key=\"%s\"", percentEncode(_consumerKey)); Poco::format(authorization, ", oauth_signature=\"%s\"", percentEncode(signature)); authorization += ", oauth_signature_method=\"PLAINTEXT\""; if (!_token.empty()) { Poco::format(authorization, ", oauth_token=\"%s\"", percentEncode(_token)); } if (!_callback.empty()) { Poco::format(authorization, ", oauth_callback=\"%s\"", percentEncode(_callback)); } authorization += ", oauth_version=\"1.0\""; request.set(HTTPRequest::AUTHORIZATION, authorization); } void OAuth10Credentials::signHMACSHA1(Poco::Net::HTTPRequest& request, const std::string& uri, const Poco::Net::HTMLForm& params) const { std::string nonce(_nonce); if (nonce.empty()) { nonce = createNonce(); } std::string timestamp(_timestamp); if (timestamp.empty()) { timestamp = Poco::NumberFormatter::format(Poco::Timestamp().epochTime()); } std::string signature(createSignature(request, uri, params, nonce, timestamp)); std::string authorization(SCHEME); if (!_realm.empty()) { Poco::format(authorization, " realm=\"%s\",", _realm); } Poco::format(authorization, " oauth_consumer_key=\"%s\"", percentEncode(_consumerKey)); Poco::format(authorization, ", oauth_nonce=\"%s\"", percentEncode(nonce)); Poco::format(authorization, ", oauth_signature=\"%s\"", percentEncode(signature)); authorization += ", oauth_signature_method=\"HMAC-SHA1\""; Poco::format(authorization, ", oauth_timestamp=\"%s\"", timestamp); if (!_token.empty()) { Poco::format(authorization, ", oauth_token=\"%s\"", percentEncode(_token)); } if (!_callback.empty()) { Poco::format(authorization, ", oauth_callback=\"%s\"", percentEncode(_callback)); } authorization += ", oauth_version=\"1.0\""; request.set(HTTPRequest::AUTHORIZATION, authorization); } 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, "+/=", ""); } std::string OAuth10Credentials::createSignature(const Poco::Net::HTTPRequest& request, const std::string& uri, const Poco::Net::HTMLForm& params, const std::string& nonce, const std::string& timestamp) const { std::map 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 (const auto& p: params) { paramsMap[percentEncode(p.first)] = percentEncode(p.second); } std::string paramsString; for (auto it = paramsMap.begin(); it != paramsMap.end(); ++it) { if (it != paramsMap.begin()) paramsString += '&'; paramsString += it->first; paramsString += "="; paramsString += it->second; } std::string signatureBase = request.getMethod(); signatureBase += '&'; signatureBase += percentEncode(uri); signatureBase += '&'; signatureBase += percentEncode(paramsString); std::string signingKey; signingKey += percentEncode(_consumerSecret); signingKey += '&'; signingKey += percentEncode(_tokenSecret); Poco::HMACEngine hmacEngine(signingKey); hmacEngine.update(signatureBase); Poco::DigestEngine::Digest digest = hmacEngine.digest(); std::ostringstream digestBase64; Poco::Base64Encoder base64Encoder(digestBase64); base64Encoder.write(reinterpret_cast(&digest[0]), digest.size()); base64Encoder.close(); return digestBase64.str(); } std::string OAuth10Credentials::percentEncode(const std::string& str) { std::string encoded; Poco::URI::encode(str, "!?#/'\",;:$&()[]*+=@", encoded); return encoded; } } } // namespace Poco::Net