spectrum2/3rdparty/cpprestsdk/tests/functional/http/client/oauth2_tests.cpp
2015-11-19 15:19:14 +01:00

383 lines
14 KiB
C++

/***
* ==++==
*
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* ==--==
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
*
* Test cases for oauth2.
*
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
****/
#include "stdafx.h"
#include "cpprest/details/http_helpers.h"
using namespace web;
using namespace web::http;
using namespace web::http::client;
using namespace web::http::details;
using namespace web::http::oauth2::experimental;
using namespace utility;
using namespace concurrency;
using namespace tests::functional::http::utilities;
extern utility::string_t _to_base64(const unsigned char *ptr, size_t size);
namespace tests { namespace functional { namespace http { namespace client
{
static std::vector<unsigned char> to_body_data(utility::string_t str)
{
const std::string utf8(conversions::to_utf8string(std::move(str)));
return std::vector<unsigned char>(utf8.data(), utf8.data() + utf8.size());
}
static bool is_application_x_www_form_urlencoded(test_request *request)
{
const auto content_type(request->m_headers[header_names::content_type]);
return (0 == content_type.find(mime_types::application_x_www_form_urlencoded));
}
SUITE(oauth2_tests)
{
struct oauth2_test_setup
{
oauth2_test_setup() :
m_uri(U("http://localhost:16743/")),
m_oauth2_config(U("123ABC"), U("456DEF"), U("https://test1"), m_uri.to_string(), U("https://bar")),
m_scoped(m_uri)
{}
web::http::uri m_uri;
oauth2_config m_oauth2_config;
test_http_server::scoped_server m_scoped;
};
#define TEST_ACCESSOR(value_, name_) \
t.set_ ## name_(value_); \
VERIFY_ARE_EQUAL(value_, t.name_());
TEST(oauth2_token_accessors)
{
oauth2_token t;
TEST_ACCESSOR(U("b%20456"), access_token)
TEST_ACCESSOR(U("a%123"), refresh_token)
TEST_ACCESSOR(U("b%20456"), token_type)
TEST_ACCESSOR(U("ad.ww xyz"), scope)
TEST_ACCESSOR(0, expires_in)
TEST_ACCESSOR(123, expires_in)
}
TEST(oauth2_config_accessors)
{
oauth2_config t(U(""), U(""), U(""), U(""), U(""));
TEST_ACCESSOR(U("ABC123abc"), client_key)
TEST_ACCESSOR(U("123abcABC"), client_secret)
TEST_ACCESSOR(U("x:/t/a?q=c&a#3"), auth_endpoint)
TEST_ACCESSOR(U("y:///?a=21#1=2"), token_endpoint)
TEST_ACCESSOR(U("z://?=#"), redirect_uri)
TEST_ACCESSOR(U("xyzw=stuv"), scope)
TEST_ACCESSOR(U("1234567890"), state)
TEST_ACCESSOR(U("keyx"), access_token_key)
TEST_ACCESSOR(true, implicit_grant)
TEST_ACCESSOR(false, implicit_grant)
TEST_ACCESSOR(true, bearer_auth)
TEST_ACCESSOR(false, bearer_auth)
TEST_ACCESSOR(true, http_basic_auth)
TEST_ACCESSOR(false, http_basic_auth)
}
#undef TEST_ACCESSOR
TEST(oauth2_build_authorization_uri)
{
oauth2_config config(U(""), U(""), U(""), U(""), U(""));
config.set_state(U("xyzzy"));
config.set_implicit_grant(false);
// Empty authorization URI.
{
VERIFY_ARE_EQUAL(U("/?response_type=code&client_id=&redirect_uri=&state=xyzzy"),
config.build_authorization_uri(false));
}
// Authorization URI with scope parameter.
{
config.set_scope(U("testing_123"));
VERIFY_ARE_EQUAL(U("/?response_type=code&client_id=&redirect_uri=&state=xyzzy&scope=testing_123"),
config.build_authorization_uri(false));
}
// Full authorization URI with scope.
{
config.set_client_key(U("4567abcd"));
config.set_auth_endpoint(U("https://test1"));
config.set_redirect_uri(U("http://localhost:8080"));
VERIFY_ARE_EQUAL(U("https://test1/?response_type=code&client_id=4567abcd&redirect_uri=http://localhost:8080&state=xyzzy&scope=testing_123"),
config.build_authorization_uri(false));
}
// Verify again with implicit grant.
{
config.set_implicit_grant(true);
VERIFY_ARE_EQUAL(U("https://test1/?response_type=token&client_id=4567abcd&redirect_uri=http://localhost:8080&state=xyzzy&scope=testing_123"),
config.build_authorization_uri(false));
}
// Verify that a new state() will be generated.
{
const uri auth_uri(config.build_authorization_uri(true));
auto params = uri::split_query(auth_uri.query());
VERIFY_ARE_NOT_EQUAL(params[U("state")], U("xyzzy"));
}
}
TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_code)
{
VERIFY_IS_FALSE(m_oauth2_config.is_enabled());
// Fetch using HTTP Basic authentication.
{
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_ARE_EQUAL(request->m_method, methods::POST);
VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request));
VERIFY_ARE_EQUAL(U("Basic MTIzQUJDOjQ1NkRFRg=="), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(to_body_data(
U("grant_type=authorization_code&code=789GHI&redirect_uri=https%3A%2F%2Fbar")),
request->m_body);
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}");
});
m_oauth2_config.token_from_code(U("789GHI")).wait();
VERIFY_ARE_EQUAL(U("xyzzy123"), m_oauth2_config.token().access_token());
VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
}
// Fetch using client key & secret in request body (x-www-form-urlencoded).
{
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request));
VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(to_body_data(
U("grant_type=authorization_code&code=789GHI&redirect_uri=https%3A%2F%2Fbar&client_id=123ABC&client_secret=456DEF")),
request->m_body);
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"xyzzy123\",\"token_type\":\"bearer\"}");
});
m_oauth2_config.set_token(oauth2_token()); // Clear token.
VERIFY_IS_FALSE(m_oauth2_config.is_enabled());
m_oauth2_config.set_http_basic_auth(false);
m_oauth2_config.token_from_code(U("789GHI")).wait();
VERIFY_ARE_EQUAL(U("xyzzy123"), m_oauth2_config.token().access_token());
VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
}
}
TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_redirected_uri)
{
// Authorization code grant.
{
m_scoped.server()->next_request().then([](test_request *request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"test1\",\"token_type\":\"bearer\"}");
});
m_oauth2_config.set_implicit_grant(false);
m_oauth2_config.set_state(U("xyzzy"));
const web::http::uri redirected_uri(m_uri.to_string() + U("?code=sesame&state=xyzzy"));
m_oauth2_config.token_from_redirected_uri(redirected_uri).wait();
VERIFY_IS_TRUE(m_oauth2_config.token().is_valid_access_token());
VERIFY_ARE_EQUAL(m_oauth2_config.token().access_token(), U("test1"));
}
// Implicit grant.
{
m_oauth2_config.set_implicit_grant(true);
const web::http::uri redirected_uri(m_uri.to_string() + U("#access_token=abcd1234&state=xyzzy"));
m_oauth2_config.token_from_redirected_uri(redirected_uri).wait();
VERIFY_IS_TRUE(m_oauth2_config.token().is_valid_access_token());
VERIFY_ARE_EQUAL(m_oauth2_config.token().access_token(), U("abcd1234"));
}
}
TEST_FIXTURE(oauth2_test_setup, oauth2_token_from_refresh)
{
oauth2_token token(U("accessing"));
token.set_refresh_token(U("refreshing"));
m_oauth2_config.set_token(token);
VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
// Verify token refresh without scope.
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_ARE_EQUAL(request->m_method, methods::POST);
VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request));
VERIFY_ARE_EQUAL(U("Basic MTIzQUJDOjQ1NkRFRg=="), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(to_body_data(
U("grant_type=refresh_token&refresh_token=refreshing")),
request->m_body);
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"ABBA\",\"refresh_token\":\"BAZ\",\"token_type\":\"bearer\"}");
});
m_oauth2_config.token_from_refresh().wait();
VERIFY_ARE_EQUAL(U("ABBA"), m_oauth2_config.token().access_token());
VERIFY_ARE_EQUAL(U("BAZ"), m_oauth2_config.token().refresh_token());
// Verify chaining refresh tokens and refresh with scope.
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_IS_TRUE(is_application_x_www_form_urlencoded(request));
VERIFY_ARE_EQUAL(to_body_data(
U("grant_type=refresh_token&refresh_token=BAZ&scope=xyzzy")),
request->m_body);
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"done\",\"token_type\":\"bearer\"}");
});
m_oauth2_config.set_scope(U("xyzzy"));
m_oauth2_config.token_from_refresh().wait();
VERIFY_ARE_EQUAL(U("done"), m_oauth2_config.token().access_token());
}
TEST_FIXTURE(oauth2_test_setup, oauth2_bearer_token)
{
m_oauth2_config.set_token(oauth2_token(U("12345678")));
http_client_config config;
// Default, bearer token in "Authorization" header (bearer_auth() == true)
{
config.set_oauth2(m_oauth2_config);
http_client client(m_uri, config);
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_ARE_EQUAL(U("Bearer 12345678"), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(U("/"), request->m_path);
request->reply(status_codes::OK);
});
http_response response = client.request(methods::GET).get();
VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
}
// Bearer token in query, default access token key (bearer_auth() == false)
{
m_oauth2_config.set_bearer_auth(false);
config.set_oauth2(m_oauth2_config);
http_client client(m_uri, config);
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(U("/?access_token=12345678"), request->m_path);
request->reply(status_codes::OK);
});
http_response response = client.request(methods::GET).get();
VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
}
// Bearer token in query, updated token, custom access token key (bearer_auth() == false)
{
m_oauth2_config.set_bearer_auth(false);
m_oauth2_config.set_access_token_key(U("open"));
m_oauth2_config.set_token(oauth2_token(U("Sesame")));
config.set_oauth2(m_oauth2_config);
http_client client(m_uri, config);
m_scoped.server()->next_request().then([](test_request *request)
{
VERIFY_ARE_EQUAL(U(""), request->m_headers[header_names::authorization]);
VERIFY_ARE_EQUAL(U("/?open=Sesame"), request->m_path);
request->reply(status_codes::OK);
});
http_response response = client.request(methods::GET).get();
VERIFY_ARE_EQUAL(status_codes::OK, response.status_code());
}
}
TEST_FIXTURE(oauth2_test_setup, oauth2_token_parsing)
{
VERIFY_IS_FALSE(m_oauth2_config.is_enabled());
// Verify reply JSON 'access_token', 'refresh_token', 'expires_in' and 'scope'.
{
m_scoped.server()->next_request().then([](test_request *request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"123\",\"refresh_token\":\"ABC\",\"token_type\":\"bearer\",\"expires_in\":12345678,\"scope\":\"baz\"}");
});
m_oauth2_config.token_from_code(U("")).wait();
VERIFY_ARE_EQUAL(U("123"), m_oauth2_config.token().access_token());
VERIFY_ARE_EQUAL(U("ABC"), m_oauth2_config.token().refresh_token());
VERIFY_ARE_EQUAL(12345678, m_oauth2_config.token().expires_in());
VERIFY_ARE_EQUAL(U("baz"), m_oauth2_config.token().scope());
VERIFY_IS_TRUE(m_oauth2_config.is_enabled());
}
// Verify undefined 'expires_in' and 'scope'.
{
m_scoped.server()->next_request().then([](test_request *request)
{
std::map<utility::string_t, utility::string_t> headers;
headers[header_names::content_type] = mime_types::application_json;
request->reply(status_codes::OK, U(""), headers, "{\"access_token\":\"123\",\"token_type\":\"bearer\"}");
});
const utility::string_t test_scope(U("wally world"));
m_oauth2_config.set_scope(test_scope);
m_oauth2_config.token_from_code(U("")).wait();
VERIFY_ARE_EQUAL(oauth2_token::undefined_expiration, m_oauth2_config.token().expires_in());
VERIFY_ARE_EQUAL(test_scope, m_oauth2_config.token().scope());
}
}
} // SUITE(oauth2_tests)
}}}}