Commit ae722898 authored by Majid Valipour's avatar Majid Valipour Committed by Chromium LUCI CQ

[WebID] Add provider API and resolve pending id request

This patch introduces the basic API for IDP to provide an id_token.
The id_token is passed to the pending id request callback.

The callback is simply held as part of UserData for the provider's
web contents. This works because we create a new web content for each
request.

To enable this without introducing new content/public changes
we moved the creation of the idp web contents to occur inside
FederatedAuthRequestImpl which also adds the callback and then passes it
to the ui dialog controller.

Bug: 1141125

Change-Id: Ic5d48642c952ac510ce8aa8cfaf53962a916f9ef
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2576432Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarKen Buchanan <kenrb@chromium.org>
Commit-Queue: Majid Valipour <majidvp@chromium.org>
Auto-Submit: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#835781}
parent 5ea9e9f6
...@@ -22,9 +22,16 @@ void ShowWebIDPermissionInfoBar( ...@@ -22,9 +22,16 @@ void ShowWebIDPermissionInfoBar(
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
void ShowWebIDSigninWindow(content::WebContents* web_contents, WebIDSigninWindow* ShowWebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url, const GURL& idp_signin_url,
base::OnceCallback<void(std::string)> on_done, content::IdentityRequestDialogController::IdProviderWindowClosedCallback
base::OnceCallback<void()> on_close) { on_done) {
NOTIMPLEMENTED();
return nullptr;
}
void CloseWebIDSigninWindow(WebIDSigninWindow* window) {
NOTIMPLEMENTED(); NOTIMPLEMENTED();
} }
...@@ -22,20 +22,25 @@ constexpr int kHeaderHeight = 75; ...@@ -22,20 +22,25 @@ constexpr int kHeaderHeight = 75;
class ModalDialog : public views::DialogDelegateView { class ModalDialog : public views::DialogDelegateView {
public: public:
ModalDialog(content::WebContents* contents, const GURL& provider) ModalDialog(content::WebContents* initiator_web_contents,
: initiator_web_contents_(contents), web_view_(nullptr) { content::WebContents* idp_web_contents,
const GURL& provider)
: initiator_web_contents_(initiator_web_contents), web_view_(nullptr) {
DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE); DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE);
SetModalType(ui::MODAL_TYPE_CHILD); SetModalType(ui::MODAL_TYPE_CHILD);
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
web_view_ = AddChildView(CreateWebView(provider)); web_view_ = AddChildView(CreateWebView(idp_web_contents, provider));
SetInitiallyFocusedView(web_view_); SetInitiallyFocusedView(web_view_);
} }
std::unique_ptr<views::WebView> CreateWebView(const GURL& provider) { std::unique_ptr<views::WebView> CreateWebView(
content::WebContents* idp_web_contents,
const GURL& provider) {
auto web_view = std::make_unique<views::WebView>( auto web_view = std::make_unique<views::WebView>(
initiator_web_contents_->GetBrowserContext()); initiator_web_contents_->GetBrowserContext());
web_view->SetWebContents(idp_web_contents);
web_view->LoadInitialURL(provider); web_view->LoadInitialURL(provider);
// The webview must get an explicitly set height otherwise the layout // The webview must get an explicitly set height otherwise the layout
...@@ -49,10 +54,11 @@ class ModalDialog : public views::DialogDelegateView { ...@@ -49,10 +54,11 @@ class ModalDialog : public views::DialogDelegateView {
return web_view; return web_view;
} }
void Show() { views::Widget* Show() {
// ShowWebModalDialogViews takes ownership of this, by way of the // ShowWebModalDialogViews takes ownership of this, by way of the
// DeleteDelegate method. // DeleteDelegate method.
constrained_window::ShowWebModalDialogViews(this, initiator_web_contents_); return constrained_window::ShowWebModalDialogViews(this,
initiator_web_contents_);
} }
private: private:
...@@ -63,14 +69,22 @@ class ModalDialog : public views::DialogDelegateView { ...@@ -63,14 +69,22 @@ class ModalDialog : public views::DialogDelegateView {
WebIDSigninWindow::WebIDSigninWindow( WebIDSigninWindow::WebIDSigninWindow(
content::WebContents* initiator_web_contents, content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider, const GURL& provider,
base::OnceCallback<void(std::string)> on_done, IdProviderWindowClosedCallback on_done) {
base::OnceCallback<void()> on_close) // TODO(majidvp): What happens if we are handling multiple concurrent WebID
: on_done_(std::move(on_done)) { // requests? At the moment we keep creating modal dialogs. This may be fine
auto* modal = new ModalDialog(initiator_web_contents, provider); // when these requests belong to different tabs but may break down if they are
// from the same tab or even share the same |initiator_web_contents| (e.g.,
modal->SetCloseCallback(std::move(on_close)); // two requests made from an iframe and its embedder frame). We need to
// TODO(majidvp): Actually call on_done callback once we have a token. // investigate this to ensure we are providing appropriate UX.
// http://crbug.com/1141125
auto* modal =
new ModalDialog(initiator_web_contents, idp_web_contents, provider);
// Set close callback to also call on_done. This ensures that if user closes
// the IDP window the caller promise is rejected accordingly.
modal->SetCloseCallback(std::move(on_done));
// ModalDialog is a WidgetDelegate, owned by its views::Widget. It is // ModalDialog is a WidgetDelegate, owned by its views::Widget. It is
// destroyed by `DeleteDelegate()` which is invoked by view hierarchy. Once // destroyed by `DeleteDelegate()` which is invoked by view hierarchy. Once
...@@ -79,15 +93,24 @@ WebIDSigninWindow::WebIDSigninWindow( ...@@ -79,15 +93,24 @@ WebIDSigninWindow::WebIDSigninWindow(
base::BindOnce([](WebIDSigninWindow* window) { delete window; }, base::BindOnce([](WebIDSigninWindow* window) { delete window; },
base::Unretained(this))); base::Unretained(this)));
modal->Show(); modal_ = modal->Show();
}
void WebIDSigninWindow::Close() {
modal_->Close();
} }
WebIDSigninWindow::~WebIDSigninWindow() = default; WebIDSigninWindow::~WebIDSigninWindow() = default;
void ShowWebIDSigninWindow(content::WebContents* initiator_web_contents, WebIDSigninWindow* ShowWebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider, const GURL& provider,
base::OnceCallback<void(std::string)> on_done, IdProviderWindowClosedCallback on_done) {
base::OnceCallback<void()> on_close) { return new WebIDSigninWindow(initiator_web_contents, idp_web_contents,
new WebIDSigninWindow(initiator_web_contents, provider, std::move(on_done), provider, std::move(on_done));
std::move(on_close)); }
void CloseWebIDSigninWindow(WebIDSigninWindow* window) {
window->Close();
} }
...@@ -16,24 +16,32 @@ namespace content { ...@@ -16,24 +16,32 @@ namespace content {
class WebContents; class WebContents;
} }
namespace views {
class Widget;
}
// The WebIDSigninWindow loads Idp sign-in page in a modal allowing user to // The WebIDSigninWindow loads Idp sign-in page in a modal allowing user to
// sign in. The modal may be closed by user or once Idp sign-in page has // sign in. The modal may be closed by user or once Idp sign-in page has
// completed its process and have called the appropriate JS callback. // completed its process and have called the appropriate JS callback.
class WebIDSigninWindow { class WebIDSigninWindow {
public: public:
// Creates and shows the modal Signin Window. It takes two callbacks: // Calls the provided callback when IDP has provided an id_token with the
// - on_done: called when IDP has provided an id_token with the id_token as // id_token a its argument, or when window is closed by user with an empty
// its parameter. // string as its argument.
// - on_close: called when window is closed.
WebIDSigninWindow(content::WebContents* initiator_web_contents, WebIDSigninWindow(content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider, const GURL& provider,
base::OnceCallback<void(std::string)> on_done, base::OnceCallback<void()> on_done);
base::OnceCallback<void()> on_close);
WebIDSigninWindow(const WebIDSigninWindow&) = delete; WebIDSigninWindow(const WebIDSigninWindow&) = delete;
WebIDSigninWindow& operator=(const WebIDSigninWindow&) = delete; WebIDSigninWindow& operator=(const WebIDSigninWindow&) = delete;
void Close();
private: private:
base::OnceCallback<void(std::string)> on_done_; // This class manages its own lifetime which is controlled by the view
// hierarchy. Once modal is deleted, this gets deleted as well.
~WebIDSigninWindow(); ~WebIDSigninWindow();
views::Widget* modal_;
}; };
#endif // CHROME_BROWSER_UI_VIEWS_WEBID_WEBID_SIGNIN_WINDOW_H_ #endif // CHROME_BROWSER_UI_VIEWS_WEBID_WEBID_SIGNIN_WINDOW_H_
...@@ -31,14 +31,25 @@ void IdentityDialogController::ShowInitialPermissionDialog( ...@@ -31,14 +31,25 @@ void IdentityDialogController::ShowInitialPermissionDialog(
} }
void IdentityDialogController::ShowIdProviderWindow( void IdentityDialogController::ShowIdProviderWindow(
content::WebContents* web_contents, content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url, const GURL& idp_signin_url,
IdProviderWindowClosedCallback callback) { IdProviderWindowClosedCallback callback) {
// TODO(majidvp): Pass in a callback to receive the token. signin_window_ =
// http://crbug.com/1141125 ShowWebIDSigninWindow(initiator_web_contents, idp_web_contents,
ShowWebIDSigninWindow(web_contents, idp_signin_url, idp_signin_url, std::move(callback));
base::OnceCallback<void(std::string)>(), }
std::move(callback));
void IdentityDialogController::CloseIdProviderWindow() {
// TODO(majidvp): This may race with user closing the signin window directly.
// So we should not really check the signin_window_ instead we should setup
// the on_close callback here here and check that to avoid lifetime issues.
if (!signin_window_)
return;
// Note that this leads to the window closed callback being run.
CloseWebIDSigninWindow(signin_window_);
signin_window_ = nullptr;
} }
void IdentityDialogController::ShowTokenExchangePermissionDialog( void IdentityDialogController::ShowTokenExchangePermissionDialog(
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
class GURL; class GURL;
class WebIDSigninWindow;
using UserApproval = content::IdentityRequestDialogController::UserApproval; using UserApproval = content::IdentityRequestDialogController::UserApproval;
using InitialApprovalCallback = using InitialApprovalCallback =
...@@ -34,12 +35,18 @@ class IdentityDialogController ...@@ -34,12 +35,18 @@ class IdentityDialogController
// content::IdentityRequestDelegate // content::IdentityRequestDelegate
void ShowInitialPermissionDialog(content::WebContents*, void ShowInitialPermissionDialog(content::WebContents*,
InitialApprovalCallback) override; InitialApprovalCallback) override;
void ShowIdProviderWindow(content::WebContents*, void ShowIdProviderWindow(content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url, const GURL& idp_signin_url,
IdProviderWindowClosedCallback) override; IdProviderWindowClosedCallback) override;
void CloseIdProviderWindow() override;
void ShowTokenExchangePermissionDialog( void ShowTokenExchangePermissionDialog(
TokenExchangeApprovalCallback) override; TokenExchangeApprovalCallback) override;
private:
// This object manages its own lifetime
WebIDSigninWindow* signin_window_;
}; };
#endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_ #endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include "content/public/browser/identity_request_dialog_controller.h" #include "content/public/browser/identity_request_dialog_controller.h"
#include "url/gurl.h" #include "url/gurl.h"
class WebIDSigninWindow;
namespace content { namespace content {
class WebContents; class WebContents;
} // namespace content } // namespace content
...@@ -25,13 +27,16 @@ void ShowWebIDPermissionInfoBar( ...@@ -25,13 +27,16 @@ void ShowWebIDPermissionInfoBar(
content::IdentityRequestDialogController::InitialApprovalCallback callback); content::IdentityRequestDialogController::InitialApprovalCallback callback);
// Creates and shows a window that loads the identity provider sign in page at // Creates and shows a window that loads the identity provider sign in page at
// the given URL. It takes two callbacks: // the given URL. The provided callback is called when IDP has provided an
// - on_done: called when IDP has provided an id_token with the id_token as // id_token with the id_token a its argument, or when window is closed by user
// its parameter. // with an empty string as its argument.
// - on_close: called when window is closed. WebIDSigninWindow* ShowWebIDSigninWindow(
void ShowWebIDSigninWindow(content::WebContents* web_contents, content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url, const GURL& idp_signin_url,
base::OnceCallback<void(std::string)> on_done, content::IdentityRequestDialogController::IdProviderWindowClosedCallback
base::OnceCallback<void()> on_close); on_done);
void CloseWebIDSigninWindow(WebIDSigninWindow* window);
#endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOGS_H_ #endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOGS_H_
...@@ -1863,6 +1863,8 @@ source_set("browser") { ...@@ -1863,6 +1863,8 @@ source_set("browser") {
"webauth/webauth_request_security_checker.h", "webauth/webauth_request_security_checker.h",
"webid/federated_auth_request_impl.cc", "webid/federated_auth_request_impl.cc",
"webid/federated_auth_request_impl.h", "webid/federated_auth_request_impl.h",
"webid/id_token_request_callback_data.cc",
"webid/id_token_request_callback_data.h",
"webid/idp_network_request_manager.cc", "webid/idp_network_request_manager.cc",
"webid/idp_network_request_manager.h", "webid/idp_network_request_manager.h",
"webrtc/webrtc_internals.cc", "webrtc/webrtc_internals.cc",
......
...@@ -7,9 +7,11 @@ ...@@ -7,9 +7,11 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "content/browser/renderer_host/render_frame_host_impl.h" #include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/webid/id_token_request_callback_data.h"
#include "content/public/common/content_client.h" #include "content/public/common/content_client.h"
#include "url/url_constants.h" #include "url/url_constants.h"
using blink::mojom::ProvideIdTokenStatus;
using blink::mojom::RequestIdTokenStatus; using blink::mojom::RequestIdTokenStatus;
namespace content { namespace content {
...@@ -48,7 +50,11 @@ FederatedAuthRequestImpl::FederatedAuthRequestImpl( ...@@ -48,7 +50,11 @@ FederatedAuthRequestImpl::FederatedAuthRequestImpl(
mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver) mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> receiver)
: FrameServiceBase(host, std::move(receiver)) {} : FrameServiceBase(host, std::move(receiver)) {}
FederatedAuthRequestImpl::~FederatedAuthRequestImpl() = default; FederatedAuthRequestImpl::~FederatedAuthRequestImpl() {
// Ensures key data members are destructed in proper order and resolves any
// pending promise.
CompleteRequest(RequestIdTokenStatus::kError, "");
}
// static // static
void FederatedAuthRequestImpl::Create( void FederatedAuthRequestImpl::Create(
...@@ -129,7 +135,7 @@ void FederatedAuthRequestImpl::OnWellKnownFetched( ...@@ -129,7 +135,7 @@ void FederatedAuthRequestImpl::OnWellKnownFetched(
// Use the web contents of the page that initiated the WebID request (i.e. // Use the web contents of the page that initiated the WebID request (i.e.
// the Relying Party) for showing the initial permission dialog. // the Relying Party) for showing the initial permission dialog.
WebContents* web_contents = WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host()); WebContents::FromRenderFrameHost(render_frame_host());
request_dialog_controller_->ShowInitialPermissionDialog( request_dialog_controller_->ShowInitialPermissionDialog(
web_contents, base::BindOnce(&FederatedAuthRequestImpl::OnSigninApproved, web_contents, base::BindOnce(&FederatedAuthRequestImpl::OnSigninApproved,
...@@ -161,11 +167,14 @@ void FederatedAuthRequestImpl::OnSigninResponseReceived( ...@@ -161,11 +167,14 @@ void FederatedAuthRequestImpl::OnSigninResponseReceived(
CompleteRequest(RequestIdTokenStatus::kError, ""); CompleteRequest(RequestIdTokenStatus::kError, "");
return; return;
} }
WebContents* web_contents = WebContents* rp_web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host()); WebContents::FromRenderFrameHost(render_frame_host());
DCHECK(!idp_web_contents_);
idp_web_contents_ = CreateIdpWebContents();
request_dialog_controller_->ShowIdProviderWindow( request_dialog_controller_->ShowIdProviderWindow(
web_contents, idp_signin_page_url, rp_web_contents, idp_web_contents_.get(), idp_signin_page_url,
base::BindOnce(&FederatedAuthRequestImpl::OnIdpPageClosed, base::BindOnce(&FederatedAuthRequestImpl::OnIdpPageClosed,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
return; return;
...@@ -184,9 +193,52 @@ void FederatedAuthRequestImpl::OnSigninResponseReceived( ...@@ -184,9 +193,52 @@ void FederatedAuthRequestImpl::OnSigninResponseReceived(
} }
} }
void FederatedAuthRequestImpl::OnTokenProvided(const std::string& id_token) {
id_token_ = id_token;
// Close the IDP window which leads to OnIdpPageClosed which is our common.
//
// TODO(majidvp): Consider if we should not wait on the IDP window closing and
// instead should directly call `OnIdpPageClosed` here.
request_dialog_controller_->CloseIdProviderWindow();
// Note that we always process the token on `OnIdpPageClosed()`.
// It is possible to get there either via:
// (a) IDP providing a token as shown below, or
// (b) User closing the sign-in window.
//
// +-----------------------+ +-------------------+ +-----------------+
// | FederatedAuthRequest | | DialogController | | IDPWebContents |
// +-----------------------+ +-------------------+ +-----------------+
// | | |
// | ShowIdProviderWindow() | |
// |-------------------------->| |
// | | |
// | | navigate to idp.com |
// | |----------------------->|
// | | |
// | | OnTokenProvided(token)|
// |<---------------------------------------------------|
// | | |
// | CloseIdProviderWindow() | |
// |-------------------------->| |
// | | |
// | closed | |
// |<--------------------------| |
// | | |
// OnIdpPageClosed() | |
// | | |
//
}
void FederatedAuthRequestImpl::OnIdpPageClosed() { void FederatedAuthRequestImpl::OnIdpPageClosed() {
// TODO(kenrb): This needs to take a token that was provided by the IDP, // This could happen if provider didn't provide any token or user closed the
// or have an abort path if none provided. // IdP window before it could.
if (id_token_.empty()) {
CompleteRequest(RequestIdTokenStatus::kError, "");
return;
}
request_dialog_controller_->ShowTokenExchangePermissionDialog( request_dialog_controller_->ShowTokenExchangePermissionDialog(
base::BindOnce(&FederatedAuthRequestImpl::OnTokenProvisionApproved, base::BindOnce(&FederatedAuthRequestImpl::OnTokenProvisionApproved,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
...@@ -199,8 +251,20 @@ void FederatedAuthRequestImpl::OnTokenProvisionApproved( ...@@ -199,8 +251,20 @@ void FederatedAuthRequestImpl::OnTokenProvisionApproved(
return; return;
} }
// TODO(kenrb): Token gets returned here. CompleteRequest(RequestIdTokenStatus::kSuccess, id_token_);
CompleteRequest(RequestIdTokenStatus::kSuccess, ""); }
std::unique_ptr<WebContents> FederatedAuthRequestImpl::CreateIdpWebContents() {
auto idp_web_contents = content::WebContents::Create(
WebContents::CreateParams(render_frame_host()->GetBrowserContext()));
// Store the callback on the provider web contents so that it can be
// used later.
IdTokenRequestCallbackData::Set(
idp_web_contents_.get(),
base::BindOnce(&FederatedAuthRequestImpl::OnTokenProvided,
weak_ptr_factory_.GetWeakPtr()));
return idp_web_contents;
} }
void FederatedAuthRequestImpl::CompleteRequest( void FederatedAuthRequestImpl::CompleteRequest(
...@@ -208,7 +272,43 @@ void FederatedAuthRequestImpl::CompleteRequest( ...@@ -208,7 +272,43 @@ void FederatedAuthRequestImpl::CompleteRequest(
const std::string& id_token) { const std::string& id_token) {
request_dialog_controller_.reset(); request_dialog_controller_.reset();
network_manager_.reset(); network_manager_.reset();
// Given that |request_dialog_controller_| has reference to this web content
// instance we destroy that first.
idp_web_contents_.reset();
if (callback_)
std::move(callback_).Run(status, id_token); std::move(callback_).Run(status, id_token);
} }
// ---- Provider logic -----
void FederatedAuthRequestImpl::ProvideIdToken(const std::string& id_token,
ProvideIdTokenCallback callback) {
WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host());
auto* request_callback_data = IdTokenRequestCallbackData::Get(web_contents);
// TODO(majidvp): This may happen if the page is not loaded by the browser's
// WebID machinery. We need a way for IDP logic to detect that and not provide
// a token. The current plan is to send a special header but we may also need
// to not expose this in JS somehow. Investigate this further.
// http://crbug.com/1141125
if (!request_callback_data) {
std::move(callback).Run(ProvideIdTokenStatus::kError);
return;
}
if (request_callback_data->Notify(id_token)) {
std::move(callback).Run(ProvideIdTokenStatus::kSuccess);
} else {
std::move(callback).Run(ProvideIdTokenStatus::kErrorTooManyResponses);
}
// After calling `Notify` it is safe to remove the callback data.
// TODO(majidvp): This is now causing a DHECK. I belive it is because we are
// not calling it on the same RunLoop as the one that set it but needs more
// investigation.
// IdTokenRequestCallbackData::Remove(web_contents);
}
} // namespace content } // namespace content
...@@ -46,6 +46,9 @@ class CONTENT_EXPORT FederatedAuthRequestImpl ...@@ -46,6 +46,9 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
const std::string& id_request, const std::string& id_request,
RequestIdTokenCallback) override; RequestIdTokenCallback) override;
void ProvideIdToken(const std::string& id_token,
ProvideIdTokenCallback) override;
private: private:
FederatedAuthRequestImpl( FederatedAuthRequestImpl(
RenderFrameHost*, RenderFrameHost*,
...@@ -57,10 +60,12 @@ class CONTENT_EXPORT FederatedAuthRequestImpl ...@@ -57,10 +60,12 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
void OnSigninApproved(IdentityRequestDialogController::UserApproval approval); void OnSigninApproved(IdentityRequestDialogController::UserApproval approval);
void OnSigninResponseReceived(IdpNetworkRequestManager::SigninResponse status, void OnSigninResponseReceived(IdpNetworkRequestManager::SigninResponse status,
const std::string& response); const std::string& response);
void OnTokenProvided(const std::string& id_token);
void OnIdpPageClosed(); void OnIdpPageClosed();
void OnTokenProvisionApproved( void OnTokenProvisionApproved(
IdentityRequestDialogController::UserApproval approval); IdentityRequestDialogController::UserApproval approval);
std::unique_ptr<WebContents> CreateIdpWebContents();
void CompleteRequest(blink::mojom::RequestIdTokenStatus, void CompleteRequest(blink::mojom::RequestIdTokenStatus,
const std::string& id_token); const std::string& id_token);
...@@ -76,6 +81,13 @@ class CONTENT_EXPORT FederatedAuthRequestImpl ...@@ -76,6 +81,13 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
// should be wrapped in a struct at that time. // should be wrapped in a struct at that time.
GURL idp_endpoint_url_; GURL idp_endpoint_url_;
// The WebContents that is used to load the IDP sign-up page. This is created
// here to allow us to setup proper callbacks on it using
// |IdTokenRequestCallbackData|. It is then passed along to chrome/browser/ui
// machinery to be used to load IDP sign-in content.
std::unique_ptr<WebContents> idp_web_contents_;
std::string id_token_;
RequestIdTokenCallback callback_; RequestIdTokenCallback callback_;
base::WeakPtrFactory<FederatedAuthRequestImpl> weak_ptr_factory_{this}; base::WeakPtrFactory<FederatedAuthRequestImpl> weak_ptr_factory_{this};
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/webid/id_token_request_callback_data.h"
#include "content/public/browser/web_contents.h"
namespace content {
constexpr char kIdTokenRequestCallbackKey[] = "kIdTokenRequestCallbackKey";
IdTokenRequestCallbackData::IdTokenRequestCallbackData(DoneCallback callback)
: callback_(std::move(callback)) {}
IdTokenRequestCallbackData::~IdTokenRequestCallbackData() = default;
bool IdTokenRequestCallbackData::Notify(const std::string& id_token) {
if (!callback_) {
return false;
}
std::move(callback_).Run(id_token);
return true;
}
// static
void IdTokenRequestCallbackData::Set(WebContents* web_contents,
DoneCallback callback) {
web_contents->SetUserData(
kIdTokenRequestCallbackKey,
std::make_unique<IdTokenRequestCallbackData>(std::move(callback)));
}
// static
IdTokenRequestCallbackData* IdTokenRequestCallbackData::Get(
WebContents* web_contents) {
return static_cast<IdTokenRequestCallbackData*>(
web_contents->GetUserData(kIdTokenRequestCallbackKey));
}
// static
void IdTokenRequestCallbackData::Remove(WebContents* web_contents) {
web_contents->RemoveUserData(kIdTokenRequestCallbackKey);
}
} // namespace content
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEBID_ID_TOKEN_REQUEST_CALLBACK_DATA_H_
#define CONTENT_BROWSER_WEBID_ID_TOKEN_REQUEST_CALLBACK_DATA_H_
#include <memory>
#include <string>
#include "base/callback.h"
#include "base/supports_user_data.h"
#include "content/common/content_export.h"
namespace content {
class WebContents;
using DoneCallback = base::OnceCallback<void(const std::string&)>;
// This class holds on to the needed WebID callbacks to help connect the IDP
// `navigator.id.provide()` response to the appropriate RP `navigator.id.get()`
// request.
//
// Note that the |web_contents| instance that is passed in must be that of the
// IDP sign-in page. An important assumption here is that this WebContents
// instance is used only for a single `navigator.id.get()` call. Otherwise the
// simple mechanism to use UserData would lead to callbacks from different WebID
// consumers overriding each leading to hard-to-detect breakage. Currently this
// assumption is valid since we create a new IDP sign-in window for each request
// and don't try to re-use them.
class CONTENT_EXPORT IdTokenRequestCallbackData
: public base::SupportsUserData::Data {
public:
explicit IdTokenRequestCallbackData(DoneCallback callback);
~IdTokenRequestCallbackData() override;
// Invoke the callback (if it is still valid) passing the provided id_token.
bool Notify(const std::string& id_token);
static void Set(WebContents* web_contents, DoneCallback callback);
static IdTokenRequestCallbackData* Get(WebContents* web_contents);
static void Remove(WebContents* web_contents);
private:
DoneCallback callback_;
};
} // namespace content
#endif // CONTENT_BROWSER_WEBID_ID_TOKEN_REQUEST_CALLBACK_DATA_H_
\ No newline at end of file
...@@ -36,13 +36,37 @@ class CONTENT_EXPORT IdentityRequestDialogController { ...@@ -36,13 +36,37 @@ class CONTENT_EXPORT IdentityRequestDialogController {
virtual ~IdentityRequestDialogController() = default; virtual ~IdentityRequestDialogController() = default;
// Permission-oriented flow methods. // Permission-oriented flow methods.
virtual void ShowInitialPermissionDialog(WebContents*,
InitialApprovalCallback) = 0; // Shows the initial permission dialog to the user. The |approval_callback|
virtual void ShowIdProviderWindow(WebContents*, // callback is called with appropriate status depending on whether user
// granted or denied the permission.
//
// 'IdentityRequestDialogController' is destroyed before
// |initiator_web_contents|.
virtual void ShowInitialPermissionDialog(
WebContents* initiator_web_contents,
InitialApprovalCallback approval_callback) = 0;
// Shows the identity provider sign-in page at the given URL using the
// |idp_web_contents| inside a modal window. The |on_closed| callback is
// called when the window is closed by user or programmatically as a result of
// invoking CloseIdProviderWindow().
//
// 'IdentityRequestDialogController' is destroyed before either WebContents.
virtual void ShowIdProviderWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url, const GURL& idp_signin_url,
IdProviderWindowClosedCallback) = 0; IdProviderWindowClosedCallback on_closed) = 0;
// Closes the identity provider sign-in window if any.
virtual void CloseIdProviderWindow() = 0;
// Shows the secondary permission dialog to the user. The |approval_callback|
// callback is called with appropriate status depending on whether user
// granted or denied the permission.
virtual void ShowTokenExchangePermissionDialog( virtual void ShowTokenExchangePermissionDialog(
TokenExchangeApprovalCallback) = 0; TokenExchangeApprovalCallback approval_callback) = 0;
}; };
} // namespace content } // namespace content
......
...@@ -18,6 +18,13 @@ enum RequestIdTokenStatus { ...@@ -18,6 +18,13 @@ enum RequestIdTokenStatus {
kError, kError,
}; };
enum ProvideIdTokenStatus {
kSuccess,
kErrorTooManyResponses,
kError,
};
// Create a federated sign-in request using the specified provider. // Create a federated sign-in request using the specified provider.
// This interface is called from a renderer process and implemented in the // This interface is called from a renderer process and implemented in the
// browser process. // browser process.
...@@ -25,5 +32,16 @@ interface FederatedAuthRequest { ...@@ -25,5 +32,16 @@ interface FederatedAuthRequest {
// Requests an IdToken to be generated, given an IDP URL and an OAuth request. // Requests an IdToken to be generated, given an IDP URL and an OAuth request.
// Returns the raw content of the IdToken. // Returns the raw content of the IdToken.
RequestIdToken(url.mojom.Url provider, string id_request) => (RequestIdTokenStatus status, string? id_token); RequestIdToken(url.mojom.Url provider, string id_request) => (RequestIdTokenStatus status, string? id_token);
// TODO(majidvp): Consider creating a whole new interface for the response
// This is cleaner and more flexible. It also avoids wasting resource by
// keeping unused state around given that we mostly expect each frame to use
// only one of these interfaces. http://crbug.com/1141125
// Provides an IdToken that is passed to the pending request. This is meant to
// be used by the IDP generating the token. Empty string resolves the pending
// request with an error.
// Returns a status indicating if the the pending RP request was resolved.
ProvideIdToken(string id_token) => (ProvideIdTokenStatus status);
}; };
...@@ -31,6 +31,17 @@ void OnRequestIdToken(ScriptPromiseResolver* resolver, ...@@ -31,6 +31,17 @@ void OnRequestIdToken(ScriptPromiseResolver* resolver,
resolver->Resolve(id_token); resolver->Resolve(id_token);
} }
void OnProvideIdToken(ScriptPromiseResolver* resolver,
mojom::blink::ProvideIdTokenStatus status) {
// TODO(kenrb): Provide better messages for different error codes.
if (status != mojom::blink::ProvideIdTokenStatus::kSuccess) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNetworkError, "Error providing the id token."));
return;
}
resolver->Resolve();
}
} // namespace } // namespace
WebID::WebID(ExecutionContext& context) WebID::WebID(ExecutionContext& context)
...@@ -39,8 +50,6 @@ WebID::WebID(ExecutionContext& context) ...@@ -39,8 +50,6 @@ WebID::WebID(ExecutionContext& context)
ScriptPromise WebID::get(ScriptState* script_state, ScriptPromise WebID::get(ScriptState* script_state,
const WebIDRequestOptions* options, const WebIDRequestOptions* options,
ExceptionState& exception_state) { ExceptionState& exception_state) {
auto* context = GetExecutionContext();
if (!options->hasProvider() || !options->hasRequest()) { if (!options->hasProvider() || !options->hasRequest()) {
exception_state.ThrowTypeError("Invalid WebIDRequestOptions"); exception_state.ThrowTypeError("Invalid WebIDRequestOptions");
return ScriptPromise(); return ScriptPromise();
...@@ -56,15 +65,7 @@ ScriptPromise WebID::get(ScriptState* script_state, ...@@ -56,15 +65,7 @@ ScriptPromise WebID::get(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
} }
if (!auth_request_.is_bound()) { BindAuthRequest();
// TODO(kenrb): Work out whether kUserInteraction is the best task type
// here. It might be appropriate to create a new one.
context->GetBrowserInterfaceBroker().GetInterface(
auth_request_.BindNewPipeAndPassReceiver(
context->GetTaskRunner(TaskType::kUserInteraction)));
auth_request_.set_disconnect_handler(
WTF::Bind(&WebID::OnConnectionError, WrapWeakPersistent(this)));
}
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise(); ScriptPromise promise = resolver->Promise();
...@@ -76,6 +77,33 @@ ScriptPromise WebID::get(ScriptState* script_state, ...@@ -76,6 +77,33 @@ ScriptPromise WebID::get(ScriptState* script_state,
return promise; return promise;
} }
ScriptPromise WebID::provide(ScriptState* script_state, String id_token) {
BindAuthRequest();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
auth_request_->ProvideIdToken(
id_token, WTF::Bind(&OnProvideIdToken, WrapPersistent(resolver)));
return promise;
}
void WebID::BindAuthRequest() {
auto* context = GetExecutionContext();
if (auth_request_.is_bound())
return;
// TODO(kenrb): Work out whether kUserInteraction is the best task type
// here. It might be appropriate to create a new one.
context->GetBrowserInterfaceBroker().GetInterface(
auth_request_.BindNewPipeAndPassReceiver(
context->GetTaskRunner(TaskType::kUserInteraction)));
auth_request_.set_disconnect_handler(
WTF::Bind(&WebID::OnConnectionError, WrapWeakPersistent(this)));
}
void WebID::Trace(blink::Visitor* visitor) const { void WebID::Trace(blink::Visitor* visitor) const {
ScriptWrappable::Trace(visitor); ScriptWrappable::Trace(visitor);
ExecutionContextClient::Trace(visitor); ExecutionContextClient::Trace(visitor);
......
...@@ -28,10 +28,12 @@ class WebID final : public ScriptWrappable, public ExecutionContextClient { ...@@ -28,10 +28,12 @@ class WebID final : public ScriptWrappable, public ExecutionContextClient {
// WebID IDL interface. // WebID IDL interface.
ScriptPromise get(ScriptState*, const WebIDRequestOptions*, ExceptionState&); ScriptPromise get(ScriptState*, const WebIDRequestOptions*, ExceptionState&);
ScriptPromise provide(ScriptState*, String id_token);
void Trace(blink::Visitor*) const override; void Trace(blink::Visitor*) const override;
private: private:
void BindAuthRequest();
void OnConnectionError(); void OnConnectionError();
HeapMojoRemote<mojom::blink::FederatedAuthRequest> auth_request_; HeapMojoRemote<mojom::blink::FederatedAuthRequest> auth_request_;
......
...@@ -11,4 +11,6 @@ ...@@ -11,4 +11,6 @@
] interface WebID { ] interface WebID {
[CallWith=ScriptState, RaisesException] [CallWith=ScriptState, RaisesException]
Promise<DOMString> get(optional WebIDRequestOptions options = {}); Promise<DOMString> get(optional WebIDRequestOptions options = {});
[CallWith=ScriptState]
Promise<void> provide(optional DOMString id_token = "");
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment