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(
NOTIMPLEMENTED();
}
void ShowWebIDSigninWindow(content::WebContents* web_contents,
const GURL& idp_signin_url,
base::OnceCallback<void(std::string)> on_done,
base::OnceCallback<void()> on_close) {
WebIDSigninWindow* ShowWebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url,
content::IdentityRequestDialogController::IdProviderWindowClosedCallback
on_done) {
NOTIMPLEMENTED();
return nullptr;
}
void CloseWebIDSigninWindow(WebIDSigninWindow* window) {
NOTIMPLEMENTED();
}
......@@ -22,20 +22,25 @@ constexpr int kHeaderHeight = 75;
class ModalDialog : public views::DialogDelegateView {
public:
ModalDialog(content::WebContents* contents, const GURL& provider)
: initiator_web_contents_(contents), web_view_(nullptr) {
ModalDialog(content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider)
: initiator_web_contents_(initiator_web_contents), web_view_(nullptr) {
DialogDelegate::SetButtons(ui::DIALOG_BUTTON_NONE);
SetModalType(ui::MODAL_TYPE_CHILD);
SetLayoutManager(std::make_unique<views::FillLayout>());
web_view_ = AddChildView(CreateWebView(provider));
web_view_ = AddChildView(CreateWebView(idp_web_contents, provider));
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>(
initiator_web_contents_->GetBrowserContext());
web_view->SetWebContents(idp_web_contents);
web_view->LoadInitialURL(provider);
// The webview must get an explicitly set height otherwise the layout
......@@ -49,10 +54,11 @@ class ModalDialog : public views::DialogDelegateView {
return web_view;
}
void Show() {
views::Widget* Show() {
// ShowWebModalDialogViews takes ownership of this, by way of the
// DeleteDelegate method.
constrained_window::ShowWebModalDialogViews(this, initiator_web_contents_);
return constrained_window::ShowWebModalDialogViews(this,
initiator_web_contents_);
}
private:
......@@ -63,14 +69,22 @@ class ModalDialog : public views::DialogDelegateView {
WebIDSigninWindow::WebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider,
base::OnceCallback<void(std::string)> on_done,
base::OnceCallback<void()> on_close)
: on_done_(std::move(on_done)) {
auto* modal = new ModalDialog(initiator_web_contents, provider);
modal->SetCloseCallback(std::move(on_close));
// TODO(majidvp): Actually call on_done callback once we have a token.
IdProviderWindowClosedCallback on_done) {
// TODO(majidvp): What happens if we are handling multiple concurrent WebID
// requests? At the moment we keep creating modal dialogs. This may be fine
// 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.,
// two requests made from an iframe and its embedder frame). We need to
// 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
// destroyed by `DeleteDelegate()` which is invoked by view hierarchy. Once
......@@ -79,15 +93,24 @@ WebIDSigninWindow::WebIDSigninWindow(
base::BindOnce([](WebIDSigninWindow* window) { delete window; },
base::Unretained(this)));
modal->Show();
modal_ = modal->Show();
}
void WebIDSigninWindow::Close() {
modal_->Close();
}
WebIDSigninWindow::~WebIDSigninWindow() = default;
void ShowWebIDSigninWindow(content::WebContents* initiator_web_contents,
const GURL& provider,
base::OnceCallback<void(std::string)> on_done,
base::OnceCallback<void()> on_close) {
new WebIDSigninWindow(initiator_web_contents, provider, std::move(on_done),
std::move(on_close));
WebIDSigninWindow* ShowWebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider,
IdProviderWindowClosedCallback on_done) {
return new WebIDSigninWindow(initiator_web_contents, idp_web_contents,
provider, std::move(on_done));
}
void CloseWebIDSigninWindow(WebIDSigninWindow* window) {
window->Close();
}
......@@ -16,24 +16,32 @@ namespace content {
class WebContents;
}
namespace views {
class Widget;
}
// 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
// completed its process and have called the appropriate JS callback.
class WebIDSigninWindow {
public:
// Creates and shows the modal Signin Window. It takes two callbacks:
// - on_done: called when IDP has provided an id_token with the id_token as
// its parameter.
// - on_close: called when window is closed.
// Calls the provided callback when IDP has provided an id_token with the
// id_token a its argument, or when window is closed by user with an empty
// string as its argument.
WebIDSigninWindow(content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& provider,
base::OnceCallback<void(std::string)> on_done,
base::OnceCallback<void()> on_close);
base::OnceCallback<void()> on_done);
WebIDSigninWindow(const WebIDSigninWindow&) = delete;
WebIDSigninWindow& operator=(const WebIDSigninWindow&) = delete;
void Close();
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();
views::Widget* modal_;
};
#endif // CHROME_BROWSER_UI_VIEWS_WEBID_WEBID_SIGNIN_WINDOW_H_
......@@ -31,14 +31,25 @@ void IdentityDialogController::ShowInitialPermissionDialog(
}
void IdentityDialogController::ShowIdProviderWindow(
content::WebContents* web_contents,
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url,
IdProviderWindowClosedCallback callback) {
// TODO(majidvp): Pass in a callback to receive the token.
// http://crbug.com/1141125
ShowWebIDSigninWindow(web_contents, idp_signin_url,
base::OnceCallback<void(std::string)>(),
std::move(callback));
signin_window_ =
ShowWebIDSigninWindow(initiator_web_contents, idp_web_contents,
idp_signin_url, 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(
......
......@@ -10,6 +10,7 @@
#include "content/public/browser/web_contents.h"
class GURL;
class WebIDSigninWindow;
using UserApproval = content::IdentityRequestDialogController::UserApproval;
using InitialApprovalCallback =
......@@ -34,12 +35,18 @@ class IdentityDialogController
// content::IdentityRequestDelegate
void ShowInitialPermissionDialog(content::WebContents*,
InitialApprovalCallback) override;
void ShowIdProviderWindow(content::WebContents*,
void ShowIdProviderWindow(content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url,
IdProviderWindowClosedCallback) override;
void CloseIdProviderWindow() override;
void ShowTokenExchangePermissionDialog(
TokenExchangeApprovalCallback) override;
private:
// This object manages its own lifetime
WebIDSigninWindow* signin_window_;
};
#endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOG_CONTROLLER_H_
......@@ -12,6 +12,8 @@
#include "content/public/browser/identity_request_dialog_controller.h"
#include "url/gurl.h"
class WebIDSigninWindow;
namespace content {
class WebContents;
} // namespace content
......@@ -25,13 +27,16 @@ void ShowWebIDPermissionInfoBar(
content::IdentityRequestDialogController::InitialApprovalCallback callback);
// Creates and shows a window that loads the identity provider sign in page at
// the given URL. It takes two callbacks:
// - on_done: called when IDP has provided an id_token with the id_token as
// its parameter.
// - on_close: called when window is closed.
void ShowWebIDSigninWindow(content::WebContents* web_contents,
const GURL& idp_signin_url,
base::OnceCallback<void(std::string)> on_done,
base::OnceCallback<void()> on_close);
// the given URL. The provided callback is called when IDP has provided an
// id_token with the id_token a its argument, or when window is closed by user
// with an empty string as its argument.
WebIDSigninWindow* ShowWebIDSigninWindow(
content::WebContents* initiator_web_contents,
content::WebContents* idp_web_contents,
const GURL& idp_signin_url,
content::IdentityRequestDialogController::IdProviderWindowClosedCallback
on_done);
void CloseWebIDSigninWindow(WebIDSigninWindow* window);
#endif // CHROME_BROWSER_UI_WEBID_IDENTITY_DIALOGS_H_
......@@ -1863,6 +1863,8 @@ source_set("browser") {
"webauth/webauth_request_security_checker.h",
"webid/federated_auth_request_impl.cc",
"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.h",
"webrtc/webrtc_internals.cc",
......
......@@ -7,9 +7,11 @@
#include "base/callback.h"
#include "base/strings/string_piece.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 "url/url_constants.h"
using blink::mojom::ProvideIdTokenStatus;
using blink::mojom::RequestIdTokenStatus;
namespace content {
......@@ -48,7 +50,11 @@ FederatedAuthRequestImpl::FederatedAuthRequestImpl(
mojo::PendingReceiver<blink::mojom::FederatedAuthRequest> 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
void FederatedAuthRequestImpl::Create(
......@@ -129,7 +135,7 @@ void FederatedAuthRequestImpl::OnWellKnownFetched(
// Use the web contents of the page that initiated the WebID request (i.e.
// the Relying Party) for showing the initial permission dialog.
WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host());
WebContents::FromRenderFrameHost(render_frame_host());
request_dialog_controller_->ShowInitialPermissionDialog(
web_contents, base::BindOnce(&FederatedAuthRequestImpl::OnSigninApproved,
......@@ -161,11 +167,14 @@ void FederatedAuthRequestImpl::OnSigninResponseReceived(
CompleteRequest(RequestIdTokenStatus::kError, "");
return;
}
WebContents* web_contents =
content::WebContents::FromRenderFrameHost(render_frame_host());
WebContents* rp_web_contents =
WebContents::FromRenderFrameHost(render_frame_host());
DCHECK(!idp_web_contents_);
idp_web_contents_ = CreateIdpWebContents();
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,
weak_ptr_factory_.GetWeakPtr()));
return;
......@@ -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() {
// TODO(kenrb): This needs to take a token that was provided by the IDP,
// or have an abort path if none provided.
// This could happen if provider didn't provide any token or user closed the
// IdP window before it could.
if (id_token_.empty()) {
CompleteRequest(RequestIdTokenStatus::kError, "");
return;
}
request_dialog_controller_->ShowTokenExchangePermissionDialog(
base::BindOnce(&FederatedAuthRequestImpl::OnTokenProvisionApproved,
weak_ptr_factory_.GetWeakPtr()));
......@@ -199,8 +251,20 @@ void FederatedAuthRequestImpl::OnTokenProvisionApproved(
return;
}
// TODO(kenrb): Token gets returned here.
CompleteRequest(RequestIdTokenStatus::kSuccess, "");
CompleteRequest(RequestIdTokenStatus::kSuccess, id_token_);
}
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(
......@@ -208,7 +272,43 @@ void FederatedAuthRequestImpl::CompleteRequest(
const std::string& id_token) {
request_dialog_controller_.reset();
network_manager_.reset();
std::move(callback_).Run(status, id_token);
// 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);
}
// ---- 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
......@@ -46,6 +46,9 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
const std::string& id_request,
RequestIdTokenCallback) override;
void ProvideIdToken(const std::string& id_token,
ProvideIdTokenCallback) override;
private:
FederatedAuthRequestImpl(
RenderFrameHost*,
......@@ -57,10 +60,12 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
void OnSigninApproved(IdentityRequestDialogController::UserApproval approval);
void OnSigninResponseReceived(IdpNetworkRequestManager::SigninResponse status,
const std::string& response);
void OnTokenProvided(const std::string& id_token);
void OnIdpPageClosed();
void OnTokenProvisionApproved(
IdentityRequestDialogController::UserApproval approval);
std::unique_ptr<WebContents> CreateIdpWebContents();
void CompleteRequest(blink::mojom::RequestIdTokenStatus,
const std::string& id_token);
......@@ -76,6 +81,13 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
// should be wrapped in a struct at that time.
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_;
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 {
virtual ~IdentityRequestDialogController() = default;
// Permission-oriented flow methods.
virtual void ShowInitialPermissionDialog(WebContents*,
InitialApprovalCallback) = 0;
virtual void ShowIdProviderWindow(WebContents*,
const GURL& idp_signin_url,
IdProviderWindowClosedCallback) = 0;
// Shows the initial permission dialog to the user. The |approval_callback|
// 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,
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(
TokenExchangeApprovalCallback) = 0;
TokenExchangeApprovalCallback approval_callback) = 0;
};
} // namespace content
......
......@@ -18,6 +18,13 @@ enum RequestIdTokenStatus {
kError,
};
enum ProvideIdTokenStatus {
kSuccess,
kErrorTooManyResponses,
kError,
};
// Create a federated sign-in request using the specified provider.
// This interface is called from a renderer process and implemented in the
// browser process.
......@@ -25,5 +32,16 @@ interface FederatedAuthRequest {
// Requests an IdToken to be generated, given an IDP URL and an OAuth request.
// Returns the raw content of the IdToken.
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,
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
WebID::WebID(ExecutionContext& context)
......@@ -39,8 +50,6 @@ WebID::WebID(ExecutionContext& context)
ScriptPromise WebID::get(ScriptState* script_state,
const WebIDRequestOptions* options,
ExceptionState& exception_state) {
auto* context = GetExecutionContext();
if (!options->hasProvider() || !options->hasRequest()) {
exception_state.ThrowTypeError("Invalid WebIDRequestOptions");
return ScriptPromise();
......@@ -56,15 +65,7 @@ ScriptPromise WebID::get(ScriptState* script_state,
return ScriptPromise();
}
if (!auth_request_.is_bound()) {
// 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)));
}
BindAuthRequest();
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
......@@ -76,6 +77,33 @@ ScriptPromise WebID::get(ScriptState* script_state,
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 {
ScriptWrappable::Trace(visitor);
ExecutionContextClient::Trace(visitor);
......
......@@ -28,10 +28,12 @@ class WebID final : public ScriptWrappable, public ExecutionContextClient {
// WebID IDL interface.
ScriptPromise get(ScriptState*, const WebIDRequestOptions*, ExceptionState&);
ScriptPromise provide(ScriptState*, String id_token);
void Trace(blink::Visitor*) const override;
private:
void BindAuthRequest();
void OnConnectionError();
HeapMojoRemote<mojom::blink::FederatedAuthRequest> auth_request_;
......
......@@ -11,4 +11,6 @@
] interface WebID {
[CallWith=ScriptState, RaisesException]
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