mirror of
https://github.com/pocoproject/poco.git
synced 2024-12-13 02:22:57 +01:00
366 lines
9.9 KiB
C++
366 lines
9.9 KiB
C++
//
|
|
// 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 <map>
|
|
#include <sstream>
|
|
|
|
|
|
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<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 (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<Poco::SHA1Engine> hmacEngine(signingKey);
|
|
hmacEngine.update(signatureBase);
|
|
Poco::DigestEngine::Digest digest = hmacEngine.digest();
|
|
std::ostringstream digestBase64;
|
|
Poco::Base64Encoder base64Encoder(digestBase64);
|
|
base64Encoder.write(reinterpret_cast<char*>(&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
|