added support for OAuth 1.0A authentication

This commit is contained in:
Guenter Obiltschnig
2014-11-10 22:15:02 +01:00
parent 420c3b55a7
commit 362eaad5cb
8 changed files with 958 additions and 5 deletions

View File

@@ -32,7 +32,8 @@ objects = \
ICMPSocket ICMPSocketImpl ICMPv4PacketImpl \
NTPClient NTPEventArgs NTPPacket \
RemoteSyslogChannel RemoteSyslogListener SMTPChannel \
WebSocket WebSocketImpl
WebSocket WebSocketImpl \
OAuth10Credentials
target = PocoNet
target_version = $(LIBVERSION)

View File

@@ -0,0 +1,274 @@
//
// OAuth10Credentials.h
//
// $Id$
//
// Library: Net
// Package: OAuth
// Module: OAuth10Credentials
//
// Definition of the OAuth10Credentials class.
//
// Copyright (c) 2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef Net_OAuth10Credentials_INCLUDED
#define Net_OAuth10Credentials_INCLUDED
#include "Poco/Net/Net.h"
#include "Poco/URI.h"
namespace Poco {
namespace Net {
class HTTPRequest;
class HTMLForm;
class Net_API OAuth10Credentials
/// This class implements OAuth 1.0A authentication for HTTP requests,
/// according to RFC 5849.
///
/// Only PLAINTEXT and HMAC-SHA1 signature methods are
/// supported. The RSA-SHA1 signature method is not supported.
///
/// The OAuth10Credentials can be used to sign a client request, as
/// well as to verify the signature of a request on the server.
///
/// To sign a client request, using a known consumer (client) key, consumer (client) secret,
/// OAuth token and token secret:
///
/// 1. Create an OAuth10Credentials object using all four credentials, either using
/// the four argument constructor, or by using the default constructor and setting
/// the credentials using the setter methods.
/// 2. Create a URI containing the full request URI.
/// 3. Create an appropriate HTTPRequest object.
/// 4. Optionally, create a HTMLForm object containing additional parameters to sign.
/// 5. Sign the request by calling authenticate(). This will add an OAuth
/// Authorization header to the request.
/// 6. Send the request using a HTTPClientSession.
///
/// To request the OAuth request token from a server, using only the consumer (client) key
/// and consumer (client) secret:
///
/// 1. Create an OAuth10Credentials object using the two consumer credentials, either using
/// the two argument constructor, or by using the default constructor and setting
/// the credentials using the setter methods.
/// 2. Specify the callback URI using setCallback().
/// 3. Create a URI containing the full request URI to obtain the token.
/// 4. Create an appropriate HTTPRequest object.
/// 5. Sign the request by calling authenticate(). This will add an OAuth
/// Authorization header to the request.
/// 6. Send the request using a HTTPClientSession.
/// 7. The response will contain the request token and request token secret.
/// These can be extracted from the response body using a HTMLForm object.
///
/// Requesting the access token and secret (temporary credentials) from the server
/// is analogous to signing a client request using consumer key, consumer secret,
/// request token and request token secret.
/// The server response will contain the access token and access token secret,
/// which can again be extracted from the response body using a HTMLForm object.
///
/// To verify a request on the server:
///
/// 1. Create an OAuth10Credentials object using the constructor taking a
/// HTTPRequest object. This will extract the consumer key and token (if
/// provided).
/// 2. Provide the consumer secret and token secret (if required) matching the
/// consumer key and token to the OAuth10Credentials object using the
/// setter methods.
/// 3. Create an URI object containing the full request URI.
/// 4. Call verify() to verify the signature.
/// 5. If verification was successful, and the request was a request for
/// a request (temporary) token, call getCallback() to
/// obtain the callback URI provided by the client.
{
public:
enum SignatureMethod
/// OAuth 1.0A Signature Method.
{
SIGN_PLAINTEXT, /// OAuth 1.0A PLAINTEXT signature method
SIGN_HMAC_SHA1 /// OAuth 1.0A HMAC-SHA1 signature method
};
OAuth10Credentials();
/// Creates an empty OAuth10Credentials object.
OAuth10Credentials(const std::string& consumerKey, const std::string& consumerSecret);
/// Creates an HTTPCredentials object with the given consumer key and consumer secret.
///
/// The token and tokenSecret will be left empty.
OAuth10Credentials(const std::string& consumerKey, const std::string& consumerSecret, const std::string& token, const std::string& tokenSecret);
/// Creates an HTTPCredentials object with the given consumer key and
/// consumer secret, as well as token and token secret.
explicit OAuth10Credentials(const HTTPRequest& request);
/// Creates an OAuth10Credentials object from a HTTPRequest object.
///
/// Extracts consumer key and token (if available) from the Authorization header.
///
/// Throws a NotAuthenticatedException if the request does
/// not contain OAuth 1.0 credentials.
~OAuth10Credentials();
/// Destroys the HTTPCredentials.
void setConsumerKey(const std::string& consumerKey);
/// Sets the consumer key.
const std::string& getConsumerKey() const;
/// Returns the consumer key.
void setConsumerSecret(const std::string& consumerSecret);
/// Sets the consumer secret.
const std::string& getConsumerSecret() const;
/// Returns the consumer secret.
void setToken(const std::string& token);
/// Sets the token.
const std::string& getToken() const;
/// Returns the token.
void setTokenSecret(const std::string& tokenSecret);
/// Sets the token.
const std::string& getTokenSecret() const;
/// Returns the token secret.
void setRealm(const std::string& realm);
/// Sets the optional realm to be included in the Authorization header.
const std::string& getRealm() const;
/// Returns the optional realm to be included in the Authorization header.
void setCallback(const std::string& uri);
/// Sets the callback URI.
const std::string& getCallback() const;
/// Returns the callback URI.
void authenticate(HTTPRequest& request, const Poco::URI& uri, SignatureMethod method = SIGN_HMAC_SHA1);
/// Adds an OAuth 1.0A Authentication header to the given request, using
/// the given signature method.
void authenticate(HTTPRequest& request, const Poco::URI& uri, const Poco::Net::HTMLForm& params, SignatureMethod method = SIGN_HMAC_SHA1);
/// Adds an OAuth 1.0A Authentication header to the given request, using
/// the given signature method.
bool verify(const HTTPRequest& request, const Poco::URI& uri);
/// Verifies the signature of the given request.
///
/// The consumer key, consumer secret, token and token secret must have been set.
///
/// Returns true if the signature is valid, otherwise false.
///
/// Throws a NotAuthenticatedException if the request does not contain OAuth
/// credentials, or in case of an unsupported OAuth version or signature method.
bool verify(const HTTPRequest& request, const Poco::URI& uri, const Poco::Net::HTMLForm& params);
/// Verifies the signature of the given request.
///
/// The consumer key, consumer secret, token and token secret must have been set.
///
/// Returns true if the signature is valid, otherwise false.
///
/// Throws a NotAuthenticatedException if the request does not contain OAuth
/// credentials, or in case of an unsupported OAuth version or signature method.
void nonceAndTimestampForTesting(const std::string& nonce, const std::string& timestamp);
/// Sets the nonce and timestamp to a wellknown value.
///
/// For use by testsuite only, to test the signature
/// algorithm with wellknown inputs.
///
/// In normal operation, the nonce is a random value
/// computed by createNonce() and the timestamp is taken
/// from the system time.
protected:
void signPlaintext(Poco::Net::HTTPRequest& request) const;
/// Signs the given HTTP request according to OAuth 1.0A PLAINTEXT signature method.
void signHMACSHA1(Poco::Net::HTTPRequest& request, const std::string& uri, const Poco::Net::HTMLForm& params) const;
/// Signs the given HTTP request according to OAuth 1.0A HMAC-SHA1 signature method.
std::string createNonce() const;
/// Creates a nonce, which is basically a Base64-encoded 32 character random
/// string, with non-alphanumeric characters removed.
std::string createSignature(const Poco::Net::HTTPRequest& request, const std::string& uri, const Poco::Net::HTMLForm& params, const std::string& nonce, const std::string& timestamp) const;
/// Creates a OAuth signature for the given request and its parameters, according
/// to <https://dev.twitter.com/docs/auth/creating-signature>.
static std::string percentEncode(const std::string& str);
/// Percent-encodes the given string according to Twitter API's rules,
/// given in <https://dev.twitter.com/docs/auth/percent-encoding-parameters>.
private:
OAuth10Credentials(const OAuth10Credentials&);
OAuth10Credentials& operator = (const OAuth10Credentials&);
std::string _consumerKey;
std::string _consumerSecret;
std::string _token;
std::string _tokenSecret;
std::string _callback;
std::string _realm;
std::string _nonce;
std::string _timestamp;
};
//
// inlines
//
inline const std::string& OAuth10Credentials::getConsumerKey() const
{
return _consumerKey;
}
inline const std::string& OAuth10Credentials::getConsumerSecret() const
{
return _consumerSecret;
}
inline const std::string& OAuth10Credentials::getToken() const
{
return _token;
}
inline const std::string& OAuth10Credentials::getTokenSecret() const
{
return _tokenSecret;
}
inline const std::string& OAuth10Credentials::getRealm() const
{
return _realm;
}
inline const std::string& OAuth10Credentials::getCallback() const
{
return _callback;
}
} } // namespace Poco::Net
#endif // Net_OAuth10Credentials_INCLUDED

View File

@@ -203,7 +203,7 @@ void HTTPAuthenticationParams::parse(std::string::const_iterator first, std::str
switch (state)
{
case STATE_SPACE:
if (Ascii::isAlphaNumeric(*it))
if (Ascii::isAlphaNumeric(*it) || *it == '_')
{
token += *it;
state = STATE_TOKEN;
@@ -220,7 +220,7 @@ void HTTPAuthenticationParams::parse(std::string::const_iterator first, std::str
{
state = STATE_EQUALS;
}
else if (Ascii::isAlphaNumeric(*it))
else if (Ascii::isAlphaNumeric(*it) || *it == '_')
{
token += *it;
}
@@ -228,7 +228,7 @@ void HTTPAuthenticationParams::parse(std::string::const_iterator first, std::str
break;
case STATE_EQUALS:
if (Ascii::isAlphaNumeric(*it))
if (Ascii::isAlphaNumeric(*it) || *it == '_')
{
value += *it;
state = STATE_VALUE;

View File

@@ -0,0 +1,364 @@
//
// OAuth10Credentials.cpp
//
// $Id$
//
// 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/HTTPResponse.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 <map>
#include <sstream>
namespace Poco {
namespace Net {
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 (authScheme == "OAuth")
{
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 (authScheme == "OAuth")
{
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 (method == "PLAINTEXT")
{
refSignature = percentEncode(_consumerSecret);
refSignature += '&';
refSignature += percentEncode(_tokenSecret);
}
else if (method == "HMAC-SHA1")
{
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("OAuth");
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("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("OAuth");
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("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 (Poco::Net::HTMLForm::ConstIterator it = params.begin(); it != params.end(); ++it)
{
paramsMap[percentEncode(it->first)] = percentEncode(it->second);
}
std::string paramsString;
for (std::map<std::string, std::string>::const_iterator 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

View File

@@ -27,7 +27,8 @@ objects = \
RawSocketTest ICMPClientTest ICMPSocketTest ICMPClientTestSuite \
NTPClientTest NTPClientTestSuite \
WebSocketTest WebSocketTestSuite \
SyslogTest
SyslogTest \
OAuth10CredentialsTest
target = testrunner
target_version = 1

View File

@@ -15,6 +15,7 @@
#include "HTTPResponseTest.h"
#include "HTTPCookieTest.h"
#include "HTTPCredentialsTest.h"
#include "OAuth10CredentialsTest.h"
CppUnit::Test* HTTPTestSuite::suite()
@@ -25,6 +26,7 @@ CppUnit::Test* HTTPTestSuite::suite()
pSuite->addTest(HTTPResponseTest::suite());
pSuite->addTest(HTTPCookieTest::suite());
pSuite->addTest(HTTPCredentialsTest::suite());
pSuite->addTest(OAuth10CredentialsTest::suite());
return pSuite;
}

View File

@@ -0,0 +1,266 @@
//
// OAuth10CredentialsTest.cpp
//
// $Id: //poco/1.4/Net/testsuite/src/HTTPCredentialsTest.cpp#3 $
//
// Copyright (c) 2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#include "OAuth10CredentialsTest.h"
#include "CppUnit/TestCaller.h"
#include "CppUnit/TestSuite.h"
#include "Poco/Net/HTTPRequest.h"
#include "Poco/Net/HTTPResponse.h"
#include "Poco/Net/OAuth10Credentials.h"
#include "Poco/Net/NetException.h"
#include "Poco/Net/HTMLForm.h"
#include "Poco/URI.h"
using Poco::Net::HTTPRequest;
using Poco::Net::HTTPResponse;
using Poco::Net::OAuth10Credentials;
using Poco::Net::NotAuthenticatedException;
using Poco::Net::HTMLForm;
using Poco::URI;
OAuth10CredentialsTest::OAuth10CredentialsTest(const std::string& name): CppUnit::TestCase(name)
{
}
OAuth10CredentialsTest::~OAuth10CredentialsTest()
{
}
void OAuth10CredentialsTest::testCallback()
{
// Note: Request taken from <https://dev.twitter.com/web/sign-in/implementing>
//
// POST /oauth/request_token HTTP/1.1
// Host: api.twitter.com
// Authorization:
// OAuth oauth_callback="http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F",
// oauth_consumer_key="cChZNFj6T5R0TigYB9yd1w",
// oauth_nonce="ea9ec8429b68d6b77cd5600adbbb0456",
// oauth_signature="F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D",
// oauth_signature_method="HMAC-SHA1",
// oauth_timestamp="1318467427",
// oauth_version="1.0"
URI uri("https://api.twitter.com/oauth/request_token");
OAuth10Credentials creds("cChZNFj6T5R0TigYB9yd1w", "L8qq9PZyRg6ieKGEKhZolGC0vJWLw8iEJ88DRdyOg");
creds.setCallback("http://localhost/sign-in-with-twitter/");
creds.nonceAndTimestampForTesting("ea9ec8429b68d6b77cd5600adbbb0456", "1318467427");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
creds.authenticate(request, uri);
std::string auth = request.get("Authorization");
assert (auth == "OAuth"
" oauth_consumer_key=\"cChZNFj6T5R0TigYB9yd1w\","
" oauth_nonce=\"ea9ec8429b68d6b77cd5600adbbb0456\","
" oauth_signature=\"F1Li3tvehgcraF8DMJ7OyxO4w9Y%3D\","
" oauth_signature_method=\"HMAC-SHA1\","
" oauth_timestamp=\"1318467427\","
" oauth_callback=\"http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F\","
" oauth_version=\"1.0\"");
}
void OAuth10CredentialsTest::testParams()
{
// Note: Request taken from <https://dev.twitter.com/oauth/overview/authorizing-requests>
// and <https://dev.twitter.com/oauth/overview/creating-signatures>.
//
// POST /1/statuses/update.json?include_entities=true HTTP/1.1
// Content-Type: application/x-www-form-urlencoded
// Authorization:
// OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
// oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
// oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
// oauth_signature_method="HMAC-SHA1",
// oauth_timestamp="1318622958",
// oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
// oauth_version="1.0"
// Content-Length: 76
// Host: api.twitter.com
//
// status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21
URI uri("https://api.twitter.com/1/statuses/update.json?include_entities=true");
OAuth10Credentials creds(
"xvz1evFS4wEEPTGEFPHBog",
"kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
"LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"
);
creds.nonceAndTimestampForTesting("kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", "1318622958");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
HTMLForm params;
params.set("include_entities", "true");
params.set("status", "Hello Ladies + Gentlemen, a signed OAuth request!");
creds.authenticate(request, uri, params);
std::string auth = request.get("Authorization");
assert (auth == "OAuth"
" oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\","
" oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\","
" oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\","
" oauth_signature_method=\"HMAC-SHA1\","
" oauth_timestamp=\"1318622958\","
" oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\","
" oauth_version=\"1.0\"");
}
void OAuth10CredentialsTest::testRealm()
{
// Note: Request taken from <https://dev.twitter.com/oauth/overview/authorizing-requests>
// and <https://dev.twitter.com/oauth/overview/creating-signatures>.
//
// POST /1/statuses/update.json?include_entities=true HTTP/1.1
// Content-Type: application/x-www-form-urlencoded
// Authorization:
// OAuth realm="Twitter API"
// oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
// oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
// oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
// oauth_signature_method="HMAC-SHA1",
// oauth_timestamp="1318622958",
// oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
// oauth_version="1.0"
// Content-Length: 76
// Host: api.twitter.com
//
// status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21
URI uri("https://api.twitter.com/1/statuses/update.json?include_entities=true");
OAuth10Credentials creds(
"xvz1evFS4wEEPTGEFPHBog",
"kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw",
"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
"LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE"
);
creds.setRealm("Twitter API");
creds.nonceAndTimestampForTesting("kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg", "1318622958");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
HTMLForm params;
params.set("include_entities", "true");
params.set("status", "Hello Ladies + Gentlemen, a signed OAuth request!");
creds.authenticate(request, uri, params);
std::string auth = request.get("Authorization");
assert (auth == "OAuth"
" realm=\"Twitter API\","
" oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\","
" oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\","
" oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\","
" oauth_signature_method=\"HMAC-SHA1\","
" oauth_timestamp=\"1318622958\","
" oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\","
" oauth_version=\"1.0\"");
}
void OAuth10CredentialsTest::testPlaintext()
{
URI uri("https://api.twitter.com/oauth/request_token");
OAuth10Credentials creds("consumerKey", "consumerSecret");
creds.setCallback("http://localhost/sign-in-with-twitter/");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
creds.authenticate(request, uri, OAuth10Credentials::SIGN_PLAINTEXT);
std::string auth = request.get("Authorization");
assert (auth == "OAuth"
" oauth_consumer_key=\"consumerKey\","
" oauth_signature=\"consumerSecret%26\","
" oauth_signature_method=\"PLAINTEXT\","
" oauth_callback=\"http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F\","
" oauth_version=\"1.0\"");
}
void OAuth10CredentialsTest::testVerify()
{
URI uri("https://api.twitter.com/1/statuses/update.json?include_entities=true");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
request.set("Authorization", "OAuth"
" oauth_consumer_key=\"xvz1evFS4wEEPTGEFPHBog\","
" oauth_nonce=\"kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg\","
" oauth_signature=\"tnnArxj06cWHq44gCs1OSKk%2FjLY%3D\","
" oauth_signature_method=\"HMAC-SHA1\","
" oauth_timestamp=\"1318622958\","
" oauth_token=\"370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb\","
" oauth_version=\"1.0\"");
OAuth10Credentials creds(request);
assert (creds.getConsumerKey() == "xvz1evFS4wEEPTGEFPHBog");
assert (creds.getToken() == "370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb");
creds.setConsumerSecret("kAcSOqF21Fu85e7zjz7ZN2U4ZRhfV3WpwPAoE3Z7kBw");
creds.setTokenSecret("LswwdoUaIvS8ltyTt5jkRh4J50vUPVVHtR2YPi5kE");
HTMLForm params;
params.read(uri.getRawQuery());
params.read("status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21");
assert (creds.verify(request, uri, params));
}
void OAuth10CredentialsTest::testVerifyPlaintext()
{
URI uri("https://api.twitter.com/oauth/request_token");
HTTPRequest request(HTTPRequest::HTTP_POST, uri.getPathEtc());
request.set("Authorization", "OAuth"
" oauth_consumer_key=\"consumerKey\","
" oauth_signature=\"consumerSecret%26\","
" oauth_signature_method=\"PLAINTEXT\","
" oauth_callback=\"http%3A%2F%2Flocalhost%2Fsign-in-with-twitter%2F\","
" oauth_version=\"1.0\"");
OAuth10Credentials creds(request);
assert (creds.getConsumerKey() == "consumerKey");
creds.setConsumerSecret("consumerSecret");
assert (creds.verify(request, uri));
assert (creds.getCallback() == "http://localhost/sign-in-with-twitter/");
}
void OAuth10CredentialsTest::setUp()
{
}
void OAuth10CredentialsTest::tearDown()
{
}
CppUnit::Test* OAuth10CredentialsTest::suite()
{
CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("OAuth10CredentialsTest");
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testCallback);
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testParams);
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testRealm);
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testPlaintext);
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testVerify);
CppUnit_addTest(pSuite, OAuth10CredentialsTest, testVerifyPlaintext);
return pSuite;
}

View File

@@ -0,0 +1,45 @@
//
// OAuth10CredentialsTest.h
//
// $Id$
//
// Definition of the OAuth10CredentialsTest class.
//
// Copyright (c) 2014, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// SPDX-License-Identifier: BSL-1.0
//
#ifndef OAuth10CredentialsTest_INCLUDED
#define OAuth10CredentialsTest_INCLUDED
#include "Poco/Net/Net.h"
#include "CppUnit/TestCase.h"
class OAuth10CredentialsTest: public CppUnit::TestCase
{
public:
OAuth10CredentialsTest(const std::string& name);
~OAuth10CredentialsTest();
void testCallback();
void testParams();
void testRealm();
void testPlaintext();
void testVerify();
void testVerifyPlaintext();
void setUp();
void tearDown();
static CppUnit::Test* suite();
private:
};
#endif // OAuth10CredentialsTest_INCLUDED