OAuth2 Authorization stage relies on a user-agent , which is typically either the system browser or an embedded user-agent such as Qt WebEngine .
The choice between system browser and an embedded user-agent depends on several factors. The following describes few main considerations:
https
or custom uri-scheme redirect URLs (see
QOAuthUriSchemeReplyHandler
). With these platforms an embedded user-agent can be used to work around the limitation.
Given these considerations, using the system browser is recommended for native applications . But as hinted by some of the points above, there may still be valid use cases for using an embedded user-agent.
Using the system browser requires opening it and navigating to the authorization URL configured by the application. Typical usage looks as follows:
connect(&m_oauth, &QAbstractOAuth::authorizeWithBrowser, this, &QDesktopServices::openUrl);
The code connects QAbstractOAuth::authorizeWithBrowser signal and QDesktopServices::openUrl slot. This opens the system browser, where user performs the necessary authentication and authorization. The application or Qt libraries have no direct control over the system browser, and it typically remains open once the authorization is concluded.
For further details and supported redirect URL schemes with system browser please see OAuth 2.0 概述 , QOAuthHttpServerReplyHandler ,和 QOAuthUriSchemeReplyHandler .
Qt WebEngine provides a web browser engine to embed web content directly into the Qt application.
Along with core control features, it comes with easy-to-use views for both QtWidgets and QtQuick applications. These views can be used as the user-agent in an OAuth2 authorization. Qt WebEngine is a large and versatile module, and the focus of this documentation is on using it with OAuth2 authorization.
There are many ways to embed the Qt WebEngine as part of the application. From practical point of view the main considerations are:
Qt WebEngine can be used with both QtQuick and QtWidgets applications for OAuth2 authorization. The main difference is in how set up the few necessary enablers.
Following illustrates a simplified QWebEngineView (QtWidget) setup. Error handling and any potential Qt WebEngine configuration is omitted for brevity.
Assuming following widgets:
QWebEngineView *webView = nullptr; QMainWindow mainWindow;
Instead of opening the system browser, we use the QWebEngineView to perform the authorization:
connect(&m_oauth, &QAbstractOAuth::authorizeWithBrowser, this, [this](const QUrl &url) { mainWindow.show(); webView->load(url); webView->show(); });
Once the authorization is finished, we close the view:
connect(&m_oauth, &QAbstractOAuth::granted, this, [this]() { // Here we use QNetworkRequestFactory to store the access token m_api.setBearerToken(m_oauth.token().toLatin1()); m_handler->close(); webView->close(); });
For QtQuick applications the flow is in principle the same, but instead of QWebEngineView widget we use WebEngineView QML element:
WebEngineView { id: authorizationWebView anchors.fill: parent visible: false }
This simplified example exposes needed APIs from C++ class
class HttpExample : public QObject { Q_OBJECT #ifdef QT_QML_LIB QML_NAMED_ELEMENT(OAuth2) #endif public: Q_INVOKABLE void authorize(); signals: void authorizationCompleted(bool success); void authorizeWithBrowser(const QUrl &url);
Which are then used on the QML-side for invoking WebEngineView to handle the authorization:
onAuthorizeWithBrowser: (url) => { console.log("Starting authorization with WebView") authorizationWebView.url = url authorizationWebView.visible = true } onAuthorizationCompleted: (success) => { console.log("Authorized: " + success); authorizationWebView.visible = false }
The choice of redirect URI scheme (
http
,
https
,或
custom-uri
scheme) has an impact how to use
Qt WebEngine
.
采用
http
loopback redirect URI and
QOAuthHttpServerReplyHandler
the handling works similarly as with system browser. Qt
WebEngine
redirects the authorization to the reply handler's localhost server similarly as the system browser.
With custom-scheme URIs (such as
com.example.myqtapp:/redirect
) 和
QOAuthUriSchemeReplyHandler
the flow works also similarly as with system browser.
The main difference is that the application does not need to be configured similarly as the Universal Links on iOS/macOS or App Links on Android , as described in QOAuthUriSchemeReplyHandler 文档编制。
m_handler.setRedirectUrl(QUrl{"com.example.myqtapp://oauth2redirect"_L1}); m_oauth.setReplyHandler(&m_handler); connect(&m_oauth, &QAbstractOAuth::authorizeWithBrowser, this, [this](const QUrl &url) { mainWindow.show(); webView->load(url); webView->show(); }); connect(&m_oauth, &QAbstractOAuth::granted, this, [this]() { // Here we use QNetworkRequestFactory to store the access token m_api.setBearerToken(m_oauth.token().toLatin1()); m_handler.close(); webView->close(); });
Technically this works so that Qt WebEngine 调用 QDesktopServices::openUrl () for unhandled URI-schemes, whose counterpart QOAuthUriSchemeReplyHandler listens to.
采用
https
URIs and
QOAuthUriSchemeReplyHandler
the logic changes slightly. Similarly as with
Custom scheme URIs
the application doesn't need to be configured, but we need to supply the redirection at the end of authorization stage to the web engine.
connect(webView, &QWebEngineView::urlChanged, this, [this](const QUrl &url){
m_handler.handleAuthorizationRedirect(url);
});
This needs to be done because from
Qt WebEngine
point of view the redirect URL is a valid
https
URL, and by default will attempt to navigate to it.
To prevent such navigation attempts and accidental authorization code exposure (consider the case the redirect URL domain isn't in your control), a more involved filtering should be used. Also the use of QOAuth2AuthorizationCodeFlow::PkceMethod is strongly recommended as it mitigates the impact of authorization code hijacking.
例如:
connect(webView->page(), &QWebEnginePage::navigationRequested, this, [this](QWebEngineNavigationRequest &request) { if (request.navigationType() == QWebEngineNavigationRequest::RedirectNavigation && m_handler.handleAuthorizationRedirect(request.url())) { request.reject(); webView->close(); } else { request.accept(); } });