Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Saving/Restoring OAuth2 tokens



  • Ok... so I've just about wrestled this into submission... I can authenticate and get myself a token properly. I've set it up so I save said token so I don't have to re-auth every time I start my app (assuming the token hasn't expired, which is checked). I've utilized the setToken() function from QOAuth2AuthorizationCodeFlow... but I see no means of setting the expiration time. I also can't refresh the token when I set it this way.... I'm not altogether positive it's actually usable when done like this. Has anyone figured out a means of saving/restoring the valid tokens for this purpose? Btw, I'm currently using Qt 5.9.0 beta...

    Also, is there a reasonably secure means of encrypting the token for storage at rest? Or the OAuth2 client data, for that matter (currently stored via a qrc-embedded json file)?

    Here's what I'm working with:

    myClass::myClass(const QString & instance, QNetworkAccessManager * nam, QObject * parent) : QOAuth2AuthorizationCodeFlow(nam, parent)
    {
    	QOAuthHttpServerReplyHandler * handler = new QOAuthHttpServerReplyHandler(8080, this);
    	QFile file(instance);
    	file.open(QIODevice::ReadOnly | QIODevice::Text);
    	Q_ASSERT(file.isOpen());
    	const QString contents = file.readAll();
    	QSettings s;
    	file.close();
    	connect(this, &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, &QDesktopServices::openUrl);	// works for android & PC
    	connect(this, &QOAuth2AuthorizationCodeFlow::requestFailed, [](const QAbstractOAuth::Error error)
    	{
    		qFatal("OAuth request failed: %d", (int)error);
    	});
    	connect(this, &QAbstractOAuth::granted, [this]()
    	{
    		QSettings s;
    		qDebug("Authorization granted...");
    		s.beginGroup("OAuth2");
    		s.setValue("token", token());
    		s.setValue("expiration", expirationAt());
    		s.endGroup();
    		qDebug("Token:%s\nExpiration:%s", qPrintable(token()), qPrintable(expirationAt().toString()));
    	});
    
    	const QJsonDocument document = QJsonDocument::fromJson(contents.toUtf8());
    	const QJsonObject object = document.object();
    	const QJsonObject settingsObject(object["web"].toObject());
    
    	setClientIdentifier(settingsObject["client_id"].toString());
    	setAuthorizationUrl(settingsObject["auth_uri"].toString());
    	setAccessTokenUrl(settingsObject["token_uri"].toString());
    	setClientIdentifierSharedKey(settingsObject["client_secret"].toString());
    	setScope("https://www.googleapis.com/auth/cloud-platform");
    	setReplyHandler(handler);
    	s.beginGroup("OAuth2");
    	QDateTime expiration = s.value("expiration").toDateTime();
    	if(expiration > QDateTime::currentDateTime())
    	{
    		qDebug("Existing token still valid...");
    		setToken(s.value("token").toString());
    		// refreshAccessToken();	// fails due to a missing "refresh" token?
    		qDebug("Token expires: %s", qPrintable(expirationAt().toString()));	// is blank/unset
    	}
    	else
    	{
    		grant();
    	}
    	s.endGroup();
    }
    


  • Anyone??


  • Lifetime Qt Champion

    Hi,

    The module being pretty new, I'd recommend posting this question on the interest mailing list. You'll find there Qt's developers/maintainers. This forum is more user oriented.



  • Hello,
    you also need the string "refreshToken"
    refreshtoken and accessToken are different,
    but setRefreshToken() and refreshToken() are not implemented.
    when granted is achieved, the variable "refreshToken" is not save. because "missing "refresh" token"


  • Banned

    This post is deleted!


  • Did you ever get this to work? I am in the same boat.



  • Did you get this to work?

    I also have the need to store the token (to avoid entering credentials each time the application starts) but haven't found an official way to do it.



  • Instead of creating a new topic, I'll just reply to this one because I want to do the same thing: (securely) storing my refresh token so I can use it to re-authenticate instead of going through the browser login process every time.

    Did anyone get this to work? I am trying to use it for TrainingPeaks which should be compliant to the OAuth2 spec. Authenticating works perfectly. Refreshing the token does not.

    When I try to use the refreshAccessToken method, the flow goes into the RefreshingAccessToken stage but nothing else happens. When I try again, I get this error:

    qt.networkauth.oauth2: Cannot refresh access token. Refresh Access Token is already in progress

    Note that this also happens when I try to refresh the token after successfully authenticating, not only when trying this after starting the application.

    Is there a way to debug this issue? I connected to all signals (that I know of) of the authentication flow but apart from the stage changing, nothing happens. I also called QLoggingCategory::setFilterRules("qt.networkauth.*=true") but nothing seems to happen.



  • In my case, the problem was that client_id and client_secret are not automatically added to the request parameters when refreshing the token.

    I added a comment to the relevant bug report: https://bugreports.qt.io/browse/QTBUG-59104



  • Got the same problem.
    Here is the way I use ton check and get or refresh my token :

    void GoogleAPI::grant(bool p_synchronousGrant)
    {
    	try
    	{
    		if (m_tokenExpiration > QDateTime::currentDateTime()) throw PFHCancelException();
    
    		quint16 port(55568);
    		QUrl authorizationUrl("https://accounts.google.com/o/oauth2/v2/auth");
    		QUrl accessTokenUrl("https://www.googleapis.com/oauth2/v4/token");
    		QString clientId("yyyyyy");
    		QString password("xxxxxx");
    
    		QOAuthHttpServerReplyHandler* replyHandler = new QOAuthHttpServerReplyHandler(port, this);
    		m_oauth2.setReplyHandler(replyHandler);
    		m_oauth2.setAuthorizationUrl(authorizationUrl);
    		m_oauth2.setAccessTokenUrl(accessTokenUrl);
    		m_oauth2.setClientIdentifier(clientId);
    		m_oauth2.setClientIdentifierSharedKey(password);
    		m_oauth2.setScope(m_scopes.join(' '));
    		m_oauth2.setRefreshToken(m_refreshToken);
    		
    
    		QSignalSpy spy(this, SIGNAL(accessGranted()));
    
    		if (!m_refreshToken.isEmpty())
    		{
    			m_oauth2.refreshAccessToken();
    		}
    		else
    		{
    			m_oauth2.grant();
    		}
    
    		if (p_synchronousGrant)
    		{
    			if (!spy.wait(60000)) throw PFHException("Google API Connexion Timeout", "");
    			else logWriter.write("Google API Connexion granted");
    		}
    	}
    	catch (PFHCancelException &p_e) {}
    	catch (PFHException &p_e) { PFHReThrow(""); }
    }
    

    When the code go through the grant() line, everything is fine and my access is granted well. But if i go through m_oauth2.refreshAccessToken(); I got this answer :

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

    What's wrong with my method ?


  • Lifetime Qt Champion

    Hi,

    Well, something wrong was sent as it seems. You might want to check the query that was sent to Google.



  • Thank you for your reply :)

    Do you have an idea of how to check the query as it's fully managed by the QOauth2authorizationcodeFlow class. I tried to add the line

    QLoggingCategory::setFilterRules("qt.networkauth.*=true");
    

    But there's no more information logged :/


  • Lifetime Qt Champion

    You could use something like wire shark to analyze the network traffic and see what is going on.



  • @JeroenDierckx thank you very much , my code works now :

    oauth->setModifyParametersFunction([&](QAbstractOAuth::Stage stage, QVariantMap* parameters)
          {
              if (stage == QAbstractOAuth::Stage::RefreshingAccessToken) {
                  parameters->insert("client_id" ,oauth->clientIdentifier());
                  parameters->insert("client_secret" oauth->clientIdentifierSharedKey());
              }
          });
    


  • @orio great. Worked for me as well!


Log in to reply