Skip to content

Prioritization Blog

Learn To Manage More Effectively With Priority Matrix

Menu
  • Web App
  • Microsoft Office Add-In
    • Prioritize Emails in Outlook
    • Manage Projects in Microsoft Teams
  • Project Management
    • Workload Management
  • For Executive Assistants
    • Top 5 templates for Exec Assistants
    • How to tackle responsibilities
    • How to be a successful EA
  • For Managers
    • How to Create 30-60-90 Day Plan
    • President Eisenhower’s Prioritization Method
    • Employee Performance Review Template
  • Contact Us
Menu

How To Authenticate with Google SSO in Qt (C++)

December 17, 2020February 7, 2023

Starting January 4, 2021, Google is blocking all single sign-on (SSO) requests using OAuth 2.0, when they come from an app’s embedded web view. This matters to you, if your native app used to show a web view to load the Google login screen, and then it used the Google cookies to operate. That flow is no longer allowed and you need to find an alternative. Here we describe how to do Google SSO with Qt.

Google SSO with Qt needs some extra work.
We use Qt in Priority Matrix for Windows, and have developed a good amount of experience with the framework. We love it, but it’s not perfect.

We are going to talk about three different topics:

  1. What Qt provides in terms of OAuth 2.0 support
  2. How to use Qt functionality to authenticate against Google
  3. How to make your native app talk to an embedded web view

Let’s get started! Along the way we will include plenty of code samples to make you can adapt the solution to your case.

Native Qt support for OAuth 2.0 flows

Google announced this change well in advance.. But if you’re caught by surprise, your first instinct would be to look at the support provided by Qt for this and other similar OAuth backends. In fact, there is a module added in Qt 5.8 called QNetworkAuth which helps with this very purpose. There is even a blog post on connecting your Qt application with Google Services using OAuth 2.0. Further, if you’d rather learn by example, you can take a look at the code for the sample Reddit and Twitter.

Limitations of the documented solutions

The provided and well documented code samples are a great start. However, they have some inherent limitations that you may be running into. If you’re reading this article, it’s perhaps because you are on the same boat. Here are some of the more common problems:

  • The Qt example suggests you use the first URL provided by Google as your redirection URI. If you do that, you app is probably not even hearing back from your browser.
  • Let’s assume you managed to get your app to respond after the OAuth flow. You may still be getting a mysterious “qoauth2authorizationcodeflow.cpp => Unexpected call” error message when you call grant(). Also a “Error transferring https://oauth2.googleapis.com/token – server replied: Bad Request”. And worst of all, this is all happening inside Qt without any practical way to debug your code.
  • In a perfect world, you have the actual OAuth flow working. Now you have to make your embedded web view invoke C++ code in your app. That’s how your app learns that the user clicked the “sign in with Google” button and wants to login. Interestingly, the modern way to embed web views in Qt using QWebEngineView does not support postMessage calls. Cocoa and other frameworks do, so we need a different way to interact with the embedded web page.

In the remainder of this post, we discuss these problems one by one. We include code samples that you can adapt for your own purposes.

How to implement Google Sign-In using QOAuth2AuthorizationCodeFlow

If you’re starting with the code samples provided by Qt, you’re off to a good start. But you will probably run into the first two problems described above. Let’s narrow down the scope of our work and solve one issue at a time. I recommend adding a button in your interface to invoke the Google SSO code. That way you don’t have to deal with capturing the event from your web view, for now. Once we are able to complete a SSO flow, you can remove the button. Then we will complete the interaction by capturing events from the web view. But let’s keep it simple for now.

Anatomy of an OAuth2 authorization flow

This is what is supposed to happen when your user wants to authenticate to your app using Google:

  1. The user clicks a “Google login” button in your app.
  2. In response, your app opens a web browser window in your computer, pointing to a Google URL.
  3. At the same time, your app opens a temporary web server listening on a custom port of your choice. Use a port number higher than 1024 to avoid needing superuser permission to run your app. In this document, we use port 1234 because, why not.
  4. The OS may ask the user for confirmation that it’s OK to let your app listen for incoming connections. Hopefully the user will approve that!
  5. The user completes the login authorization on the web browser. They tell Google that they trust your app, and then the user is redirected to a local URL as described earlier.
  6. Your app’s temporary web server detects that request which contains a login code from Google.
  7. The next hidden step is the exchange of a SSO code for a login token, done internally by Qt.
  8. If all goes well your app has a token that can be used to access whatever Google APIs you had defined in the requested scopes.

What to do if your app doesn’t hear back from Google

The first problem you may face is that your app does not notice when the login token was granted. If you follow the Qt example, you may be using the first URI that you get from the Google Developers console. That is, your SSO flow uses a redirection URL similar to urn:ietf:wg:oauth:2.0:oob. This might or might not work depending on your system. Instead, it is more reliable to use http://127.0.0.1:1234/ which points to your own machine. Note that this is preferable to http://localhost:1234/ because there is no need for a DNS resolution step. We want to eliminate as many potential pitfalls from our code. Also, make sure you keep the / at the end, because without that, some users report errors.

I like to avoid over-engineering my code at all costs. To keep things simple, we can add some simple definitions as follows:

#define CLIENT_ID "YOUR-CLIENT-ID"
#define CLIENT_SECRET "YOUR-CLIENT-SECRET"
#define AUTH_URI "https://accounts.google.com/o/oauth2/auth"
#define TOKEN_URI "https://oauth2.googleapis.com/token"
//#define REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
//#define REDIRECT_URI "http://localhost:1234/"
#define REDIRECT_URI "http://127.0.0.1:1234/"

What if my app gets a login code, but fails to get a token

Once you make the above changes to your app, it will likely hear back from Google. If not, look at the Qt console because there may be some errors like:

qt.networkauth.oauth2: Unexpected call
qt.networkauth.replyhandler: Error transferring https://oauth2.googleapis.com/token - server replied: Bad Request

This happens all inside of the Qt module, and technically you could trace the steps by loading the Qt sources. But perhaps you have some experience with these 400 errors. Your intuition might be that there’s some encoding problem. Indeedd the issue here is that the code Google sends your way is url-encoded. We need to decode the string before you can utilize it in any other context. This issue is particularly insidious because, depending factors including luck, you may sometimes get a valid code.

To address this problem consistently and reliably, use setModifyParametersFunction to intercept and modify the code parameter as shown:

this->google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
   // Percent-decode the "code" parameter so Google can match it
   if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
      QByteArray code = parameters->value("code").toByteArray();
      (*parameters)["code"] = QUrl::fromPercentEncoding(code);
   }
});

This way, Google will recognize the login code against their records, and your app will get a login token.

Show me the authentication code

Now, let’s put it all together showing our code. Here is an abridged version of our code, starting with the header:

// GoogleSSO.h
#include <QNetworkReply>
#include <QOAuth2AuthorizationCodeFlow>

class GoogleSSO : public QObject {
    Q_OBJECT
public:
    GoogleSSO(QObject *parent=nullptr);
    virtual ~GoogleSSO();

public slots:
    void authenticate();

signals:
    void gotToken(const QString& token);

private:
    QOAuth2AuthorizationCodeFlow * google;
};

…and then the implementation, with some commented-out code left in place for demonstration purposes:

// GoogleSSO.cpp
#include "googlesso.h"
#include <QString>
#include <QDir>
#include <QUrl>
#include <QOAuthHttpServerReplyHandler>
#include <QDesktopServices>

// Get these from https://console.developers.google.com/apis/credentials
#define CLIENT_ID "YOUR-CLIENT-ID"
#define CLIENT_SECRET "YOUR-CLIENT-SECRET"
#define AUTH_URI "https://accounts.google.com/o/oauth2/auth"
#define TOKEN_URI "https://oauth2.googleapis.com/token"
//#define REDIRECT_URI "urn:ietf:wg:oauth:2.0:oob"
//#define REDIRECT_URI "http://localhost"
//#define REDIRECT_URI "http://localhost:1234/"
#define REDIRECT_URI "http://127.0.0.1:1234/"

GoogleSSO::GoogleSSO(QObject *parent) : QObject(parent) {
    this->google = new QOAuth2AuthorizationCodeFlow(this);
    this->google->setScope("email");
    connect(this->google, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);

    const QUrl authUri(AUTH_URI);
    const auto clientId = CLIENT_ID;
    const QUrl tokenUri(TOKEN_URI);
    const auto clientSecret(CLIENT_SECRET);
    const QUrl redirectUri(REDIRECT_URI);
    const auto port = static_cast<quint16>(redirectUri.port());
    this->google->setAuthorizationUrl(authUri);
    this->google->setClientIdentifier(clientId);
    this->google->setAccessTokenUrl(tokenUri);
    this->google->setClientIdentifierSharedKey(clientSecret);

    this->google->setModifyParametersFunction([](QAbstractOAuth::Stage stage, QVariantMap* parameters) {
        // Percent-decode the "code" parameter so Google can match it
        if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
            QByteArray code = parameters->value("code").toByteArray();
            (*parameters)["code"] = QUrl::fromPercentEncoding(code);
        }
    });

    QOAuthHttpServerReplyHandler* replyHandler = new QOAuthHttpServerReplyHandler(port, this);
    this->google->setReplyHandler(replyHandler);

    connect(this->google, &QOAuth2AuthorizationCodeFlow::granted, [=](){
        const QString token = this->google->token();
        emit gotToken(token);

// Alternatively, just use the token for your purposes
//        auto reply = this->google->get(QUrl("https://people.googleapis.com/v1/{resourceName=people/me}"));
//        connect(reply, &QNetworkReply::finished, [reply](){
//            qInfo() << reply->readAll();
//        });
    });
}

GoogleSSO::~GoogleSSO() {
    delete this->google;
}

// Invoked externally to initiate
void GoogleSSO::authenticate() {
    this->google->grant();
}

At this point you should have a working authentication flow. But if your previous experience relied on an embedded web view, you need to somehow connect your web view with your app. That is what we are going to discuss in the following section.

How to invoke C++ code from a webview embedded in a Qt app

The straightforward, old school way to have a native app interact with an embedded web view was to let it operate independently. Then, when needed, use its QNetworkManager, or borrow its cookies into your own QNetworkCookieJar for more flexibility. However, we can’t authenticate solely using the embedded web view. Instead, the web view needs to communicate with the native app to indicate that the user wants to login. In other words, we need to connect the “Login with Google” button with the native, C++ code of our app.

What about capturing a postMessage call?

In other frameworks like Cocoa it’s possible to bubble up a JavaScript window.postMessage to the containing iframe, and that way it’s easy for the web view to communicate with your app. However when using Qt, this is not currently supported, and instead we need to use web channels, as follows.

QWebChannel to the rescue

The way QWebChannel interacts with a web view is by publishing an object that can be accessed from the web page, and therefore invoked as a regular JavaScript object, therefore opening a communication channel between the JavaScript and native/C++ codes.

The following code shows how to expose a webobj object to your web view so you can invoke it via JavaScript:

QWebEngineView* wv = prepareWebview();
QWebChannel *channel = new QWebChannel(this);
channel->registerObject("webobj", this);
wv->page()->setWebChannel(channel);
wv->load(url);

For this to work, Qt provides a qwebchannel.js file that you must load prior to attempting to communicate with the channel object. Once loaded, we can access the exposed objects from the JavaScript side, invoke its methods, and in general, communicate with the app.

The following code demonstrates this idea:

<script src="path/to/qwebchannel.js"></script>

<script>
    var googleSignInButton = document.getElementById('googleSignIn');
    if (googleSignInButton) {
      googleSignInButton.onclick = function () {
        new QWebChannel(qt.webChannelTransport, function(channel) {
          var webobj = channel.objects.webobj;
          window.webobj = webobj;
          webobj.googleSignIn();  // Run it just the once, after installing it
                }
            );
            // Alternatively, run later
            // webobj.googleSignIn();
      };
    }
</script>

The code above does several things in just a few lines:

  1. It first looks for an HTML button with id=”googleSignIn”.
  2. Then it adds an onclick handler to install the QWebChannel object and make it accessible to rest of the JavaScript code.
  3. Right at that moment, it invokes the googleSignIn() method of the exposed object. Alternatively, you can install the handler at load time, but this is a lighter flow in case the user does NOT want to invoke your SSO flow.

Conclusion

If you followed all the way here, you should have everything you need to adapt your Qt app to the new restrictions of Google’s SSO system. If you have any questions, you can use our contact form so that one of our friendly team members gets back to you promptly.

This post is a departure from our typical content in order to share some of the technical work we do at Appfluence. We expect to keep posting content like this whenever we have something interesting to say.

Related Posts:

  • Basecamp Alternatives - A List of Basecamp Alternatives
  • Recipe for Chocolate Chip Cookies in Priority Matrix
  • Why does PM need Microsoft permissions?
  • Priority Matrix: Your Omnifocus for Android
  • Finding the best to do list app with a to do list template
  • Productivity: definition and measurement (GPD vs personal)

Post navigation

← 10 Tips for Project Budgeting
Powerpoint as a Versatile Tool to Support Online Project Meetings →

Search this Blog!

Popular Posts

  • What President Eisenhower can teach us about prioritization

  • Top 10 time management tips from a Stanford Entrepreneur

  • How to be productive on a Monday morning
  • Construction management resources

Popular Tags

Action Item Lists Action Items adhd Architectural Engineer Industry bad manager brainstorming Bridge Construction Industry Civil Engineer Industry Conference conferences Construction Construction and Design Industry Construction Conference Construction Conferences Construction Industry cpm education eisenhower method employees excel excel template goals gtd How To leadership management management style manager pharmaceutical innovations prioritize tasks priority matrix productivity Project Management projects project tracking template skills small business status report templates time management Tips and Tricks to do list windows work Workload Management Template

Our Most Popular Posts

Prioritization Matrix
Eisenhower Time Management
Gantt Chart in Excel
Weekly Status Report Template
Time Management Skills for College Students
Free Swot Template
Omnifocus for Windows
SMART Goals Worksheet
Converting Goals into Action Items

ABOUT PRIORITY MATRIX

Priority Matrix is lightweight project management solution that increases visibility and accountability within teams. Manage more effectively with Priority Matrix.

NAVIGATION

  • Web App
  • Microsoft Office Add-In
    • Prioritize Emails in Outlook
    • Manage Projects in Microsoft Teams
  • Project Management
    • Workload Management
  • For Executive Assistants
    • Top 5 templates for Exec Assistants
    • How to tackle responsibilities
    • How to be a successful EA
  • For Managers
    • How to Create 30-60-90 Day Plan
    • President Eisenhower’s Prioritization Method
    • Employee Performance Review Template
  • Contact Us
© 2025 Prioritization Blog | Powered by Minimalist Blog WordPress Theme