Qt OAuth2 Browser Support

OAuth2 User-Agents

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:

  • System browser may already have active logins by the user. Therefore the user authentication during authorization stage may be more straightforward as the existing login can be used. In contrast with an embedded user-agent user typically needs to perform a new login. On the other hand, leaving a login session behind in the system browser may not always be desirable. System browsers may also share application usage data with other parties.
  • System browser is typically familiar for the user, and provides a familiar user experience for logging in. On the other hand, while an embedded user-agent may provide less familiar look-and-feel, the application developer is able to embed the login interaction as part of the application window, rather than it occurring on a separate browser window. Furthermore the application developer can automate closing of the embedded user-agent when no longer needed.
  • System browsers provide familiar security visuals, such as the address bar and certificate validation for the user. These may not be visible on an embedded user-agent. Furthermore the system browsers may better leverage security features of the underlying operating system.
  • An embedded user-agent potentially has access to all security credentials the user enters.
  • Not all platforms provide support for handling https or custom uri-scheme redirect URLs (see QOAuthUriSchemeReplyHandler). With these platforms an embedded user-agent can be used to work around the limitation.
  • Including an embedded user-agent as part of the application is typically a large component, increasing the storage footprint of the application. On the other hand, all use cases may not have a system browser available, or the application may use an embedded user-agent already for other purposes.

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 System Browser

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 Qt OAuth2 Overview, QOAuthHttpServerReplyHandler, and QOAuthUriSchemeReplyHandler.

Using Qt WebEngine

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:

  • QtQuick vs QtWidgets Applications. This impacts how to set up the necessary integration with QtNetworkAuth classes.
  • Redirect URI scheme. This impacts which QtNetworkAuth reply handler classes to use, and how (see Qt OAuth2 Overview).

QtQuick and QtWidgets Applications

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
    }

Redirect URI Schemes

The choice of redirect URI scheme (http, https, or custom-uri scheme) has an impact how to use Qt WebEngine.

http Loopback URIs

With 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.

Custom scheme URIs

With custom-scheme URIs (such as com.example.myqtapp:/redirect) and 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 documentation.

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 calls the QDesktopServices::openUrl() for unhandled URI-schemes, whose counterpart QOAuthUriSchemeReplyHandler listens to.

https URIs

With 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.

For example:

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();
    }
});

© 2024 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.