| // 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 |