blob: eaf8bd36505b99c7cb9c74f4c86e1640fbbefe4a [file] [log] [blame]
// Copyright 2017 Google Inc. 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.
#include "cobalt/xhr/xhr_modify_headers.h"
#include "base/logging.h"
#include "base/synchronization/waitable_event.h"
#include "starboard/mutex.h"
#include "starboard/shared/uwp/async_utils.h"
#include "starboard/shared/win32/wchar_utils.h"
using Windows::Security::Authentication::Web::Core::
WebAuthenticationCoreManager;
using Windows::Security::Authentication::Web::Core::WebTokenRequest;
using Windows::Security::Authentication::Web::Core::WebTokenResponse;
using Windows::Security::Authentication::Web::Core::WebTokenRequestResult;
using Windows::Security::Authentication::Web::Core::WebTokenRequestStatus;
using Windows::Security::Credentials::WebAccountProvider;
using Windows::System::UserAuthenticationStatus;
using Windows::UI::Core::CoreWindow;
using Windows::UI::Core::CoreDispatcherPriority;
using Windows::UI::Core::DispatchedHandler;
namespace sbwin32 = starboard::shared::win32;
namespace {
// The name of the header to send the STS token out on.
const char kXauthHeaderName[] = "Authorization";
// The prefix for the value of the Authorization header when there is an XAuth
// token to attach to the request. The token itself should directly follow this
// prefix.
const char kXauthHeaderPrefix[] = "XBL3.0 x=";
// The name of the header to detect on requests as a trigger to add an STS token
// for the given RelyingPartyId (the value of the header). The header itself
// will be removed from the outgoing request, and replaced with an Authorization
// header with a valid STS token for the current primary user.
const base::StringPiece kXauthTriggerHeaderName = "X-STS-RelyingPartyId";
inline std::ostream& operator<<(std::ostream& os,
const UserAuthenticationStatus& state) {
switch (state) {
case UserAuthenticationStatus::Unauthenticated:
os << "Unauthenticated";
break;
case UserAuthenticationStatus::LocallyAuthenticated:
os << "LocallyAuthenticated";
break;
case UserAuthenticationStatus::RemotelyAuthenticated:
os << "RemotelyAuthenticated";
break;
default:
os << "Unknown";
}
return os;
}
inline std::ostream& operator<<(std::ostream& os,
const WebTokenRequestStatus& status) {
switch (status) {
case WebTokenRequestStatus::Success:
os << "Success";
break;
case WebTokenRequestStatus::AccountProviderNotAvailable:
os << "Account provider is not available.";
break;
case WebTokenRequestStatus::AccountSwitch:
os << "Account associated with the request was switched.";
break;
case WebTokenRequestStatus::ProviderError:
os << "Provider Error. See Provider's documentation.";
break;
case WebTokenRequestStatus::UserCancel:
os << "User Cancel";
break;
case WebTokenRequestStatus::UserInteractionRequired:
os << "User interaction is required. Try the request with "
"RequestTokenAsync";
break;
default:
os << "Unknown case";
}
return os;
}
WebTokenRequestResult^ RequestToken(WebTokenRequest^ request) {
using starboard::shared::uwp::WaitForResult;
IAsyncOperation<WebTokenRequestResult ^> ^ request_operation = nullptr;
base::WaitableEvent request_operation_set(false, false);
// Ensure WebAuthenticationCoreManager::RequestTokenAsync is called on the
// UI thread, since documentation states that "This method cannot be called
// from background threads", per
// https://docs.microsoft.com/en-us/uwp/api/windows.security.authentication.web.core.webauthenticationcoremanager
Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow
->Dispatcher->RunAsync(
CoreDispatcherPriority::Normal,
ref new DispatchedHandler(
[&request, &request_operation_set, &request_operation] {
request_operation =
WebAuthenticationCoreManager::RequestTokenAsync(request);
request_operation_set.Signal();
}));
request_operation_set.Wait();
WebTokenRequestResult^ result = WaitForResult(request_operation);
return result;
}
bool PopulateToken(const std::string& relying_party, std::string* out) {
using starboard::shared::uwp::WaitForResult;
DCHECK(out);
WebAccountProvider^ xbox_provider =
WaitForResult(WebAuthenticationCoreManager::FindAccountProviderAsync(
"https://xsts.auth.xboxlive.com"));
WebTokenRequest^ request = ref new WebTokenRequest(xbox_provider);
Platform::String^ relying_party_cx =
sbwin32::stringToPlatformString(relying_party);
request->Properties->Insert("Url", relying_party_cx);
request->Properties->Insert("Target", "xboxlive.signin");
request->Properties->Insert("Policy", "DELEGATION");
WebTokenRequestResult^ token_result = WaitForResult(
WebAuthenticationCoreManager::GetTokenSilentlyAsync(request));
if (token_result->ResponseStatus ==
WebTokenRequestStatus::UserInteractionRequired) {
token_result = RequestToken(request);
}
if (token_result->ResponseStatus == WebTokenRequestStatus::Success) {
SB_DCHECK(token_result->ResponseData->Size == 1);
if (token_result->ResponseData->Size != 1) {
*out = "";
return false;
}
WebTokenResponse^ token_response = token_result->ResponseData->GetAt(0);
*out = sbwin32::platformStringToString(token_response->Token);
return true;
} else {
SB_DLOG(INFO) << "Response Status " << token_result->ResponseStatus;
if (token_result->ResponseError) {
unsigned int error_code = token_result->ResponseError->ErrorCode;
Platform::String^ message = token_result->ResponseError->ErrorMessage;
SB_DLOG(INFO) << "Error code: " << error_code;
SB_DLOG(INFO) << "Error message: "
<< sbwin32::platformStringToString(message);
}
*out = "";
}
return false;
}
void AppendUrlPath(const std::string& path, std::string* url_parameter) {
DCHECK(url_parameter);
if (path.empty()) {
return;
}
std::string& url(*url_parameter);
// if path starts with a '/' and url ends with a '/', remove trailing slash
// from url before appending the path
if (!url.empty() && *(url.rbegin()) == '/' && path[0] == '/') {
url.resize(url.size() - 1);
}
url.append(path);
}
} // namespace
namespace cobalt {
namespace xhr {
void CobaltXhrModifyHeader(const GURL& request_url,
net::HttpRequestHeaders* headers) {
DCHECK(headers);
std::string relying_party;
bool trigger_header_found = headers->GetHeader(kXauthTriggerHeaderName,
&relying_party);
if (!trigger_header_found) {
return;
}
if (request_url.has_path()) {
std::string request_url_path = request_url.path();
AppendUrlPath(request_url_path, &relying_party);
}
std::string out_string;
if (!PopulateToken(relying_party, &out_string)) {
return;
}
headers->RemoveHeader(kXauthTriggerHeaderName);
headers->SetHeader(kXauthHeaderName, out_string);
}
} // namespace xhr
} // namespace cobalt