Qt Forum

    • Login
    • Search
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search
    • Unsolved

    Solved Problems with Adding Oauth2 Implicit Flow Class to NetworkAuth Module

    General and Desktop
    network oauth2.0 networkauth
    2
    8
    453
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • B
      basyskom-dege last edited by

      Hello everybody,

      i want to use the Networkauth API for the Implicit Flow of Oauth2. Since there is only this Authorization Flow class, i wrote a similar class to support the Implicit Flow. I followed this Guide on how to add a new module. Since the two flows only differ slightly, i just copied most of the Authorization-Class, renamed it, removed the unnecessary parts and added all the files accordingly. I can load up the qtnetworkauth module and i can compile it with no errors. The output tells me that it compiles my new class QOAuth2ImplicitFlow and everything looks fine. But when i try to run a test that i wrote in qtnetworkauth/tests/auto/oauth2/tst_oauth2.cpp its gives me this error:

      Qt/5.15.1/Src/qtnetworkauth/tests/auto/oauth2/tst_oauth2: undefined symbol: _ZN19QOAuth2ImplicitFlowC1EP7QObject, version Qt_5
      

      When i try to use my class in the Reddit Example, it gives me some linker erorrs:

      g++ -Wl,-O1 -Wl,-rpath,/home/mdeg/Qt/5.15.1/gcc_64/lib -o redditclient main.o redditmodel.o redditwrapper.o moc_redditmodel.o moc_redditwrapper.o   /home/mdeg/Qt/5.15.1/gcc_64/lib/libQt5Widgets.so /home/mdeg/Qt/5.15.1/gcc_64/lib/libQt5Gui.so /home/mdeg/Qt/5.15.1/gcc_64/lib/libQt5NetworkAuth.so /home/mdeg/Qt/5.15.1/gcc_64/lib/libQt5Network.so /home/mdeg/Qt/5.15.1/gcc_64/lib/libQt5Core.so -lGL -lpthread   
      /usr/bin/ld: main.o: in function `RedditModel::~RedditModel()':
      main.cpp:(.text._ZN11RedditModelD2Ev[_ZN11RedditModelD5Ev]+0x6b): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: main.o: in function `RedditModel::~RedditModel()':
      main.cpp:(.text._ZN11RedditModelD0Ev[_ZN11RedditModelD5Ev]+0x6b): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: redditmodel.o: in function `RedditModel::RedditModel(QString const&, QObject*) [clone .cold]':
      redditmodel.cpp:(.text.unlikely+0x139): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: redditwrapper.o: in function `RedditWrapper::RedditWrapper(QObject*)':
      redditwrapper.cpp:(.text+0x76b): undefined reference to `QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(QObject*)'
      /usr/bin/ld: redditwrapper.o: in function `RedditWrapper::grant()':
      redditwrapper.cpp:(.text+0x4589): undefined reference to `QOAuth2ImplicitFlow::grant()'
      /usr/bin/ld: redditwrapper.o: in function `RedditWrapper::RedditWrapper(QObject*) [clone .cold]':
      redditwrapper.cpp:(.text.unlikely+0x151): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: redditwrapper.o: in function `RedditWrapper::RedditWrapper(QString const&, QObject*) [clone .cold]':
      redditwrapper.cpp:(.text.unlikely+0x1b7): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: moc_redditwrapper.o: in function `RedditWrapper::~RedditWrapper()':
      moc_redditwrapper.cpp:(.text._ZN13RedditWrapperD2Ev[_ZN13RedditWrapperD5Ev]+0x1c): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      /usr/bin/ld: moc_redditwrapper.o: in function `RedditWrapper::~RedditWrapper()':
      moc_redditwrapper.cpp:(.text._ZN13RedditWrapperD0Ev[_ZN13RedditWrapperD5Ev]+0x1c): undefined reference to `QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()'
      collect2: error: ld returned 1 exit status
      make: *** [Makefile:288: redditclient] Error 1
      16:44:21: The process "/usr/bin/make" exited with code 2.
      Error while building/deploying project redditclient (kit: Desktop Qt 5.15.1 GCC 64bit)
      When executing step "Make"
      16:44:21: Elapsed time: 00:01.
      
      

      I know its a linking error but i cant manage to fix it or find the cause for it. Am i missing something? The .pro file looks like this and should be good i guess:

      QT += widgets network networkauth
      requires(qtConfig(listview))
      
      TARGET = redditclient
      
      # Input
      SOURCES += main.cpp \
          redditmodel.cpp \
          redditwrapper.cpp
      
      HEADERS += \
          redditmodel.h \
          redditwrapper.h
      
      # install
      target.path = $$[QT_INSTALL_EXAMPLES]/oauth/redditclient
      INSTALLS += target
      
      

      I did not post my code since it is pretty big but i can upload it somewhere if it helps.

      Thanks in advance!

      1 Reply Last reply Reply Quote 0
      • SGaist
        SGaist Lifetime Qt Champion last edited by

        Hi and welcome to devnet,

        Can you share your class declaration and code ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        1 Reply Last reply Reply Quote 1
        • B
          basyskom-dege last edited by

          Thanks for the quick answer! I removed the licence comments at the top of each file so they are not that big here.

          qoauth2implicitflow.h

          #ifndef QOAUTH2IMPLICITFLOW_H
          #define QOAUTH2IMPLICITFLOW_H
          
          #include <QtNetworkAuth/qoauthglobal.h>
          #include <QtNetworkAuth/qabstractoauth2.h>
          
          QT_BEGIN_NAMESPACE
          
          class QUrl;
          class QString;
          class QNetworkAccessManager;
          
          class QOAuth2ImplicitFlowPrivate;
          class Q_OAUTH_EXPORT QOAuth2ImplicitFlow : public QAbstractOAuth2
          {
                  Q_OBJECT
          public:
              explicit QOAuth2ImplicitFlow(QObject *parent = nullptr);
              explicit QOAuth2ImplicitFlow(QNetworkAccessManager *manager,
                                           QObject *parent = nullptr);
          
              QOAuth2ImplicitFlow(const QString &clientIdentifier,
                                           QNetworkAccessManager *manager,
                                           QObject *parent = nullptr);
          
              QOAuth2ImplicitFlow(const QUrl &authorizationUrl,
                                           QNetworkAccessManager *manager,
                                           QObject *parent = nullptr);
          
              QOAuth2ImplicitFlow(const QString &clientIdentifier,
                                           const QUrl &authorizationUrl,
                                           QNetworkAccessManager *manager,
                                           QObject *parent = nullptr);
          
              ~QOAuth2ImplicitFlow();
          public Q_SLOTS:
              void grant() override;
              
          protected:
              QUrl buildAuthenticateUrl(const QVariantMap &parameters = QVariantMap());
              void resourceOwnerAuthorization(const QUrl &url,
                                              const QVariantMap &parameters = QVariantMap()) override;
          private:
              Q_DISABLE_COPY(QOAuth2ImplicitFlow)
              Q_DECLARE_PRIVATE(QOAuth2ImplicitFlow)
          };
          
          #endif // QOAUTH2IMPLICITFLOW_H
          

          qoauth2implicitflow_p.h

          #ifndef QOAUTH2IMPLICITFLOW_P_H
          #define QOAUTH2IMPLICITFLOW_P_H
          
          #ifndef QT_NO_HTTP
          
          #include <private/qabstractoauth2_p.h>
          
          #include <QtNetworkAuth/qoauthglobal.h>
          #include <QtNetworkAuth/qoauth2implicitflow.h>
          
          #include <QtCore/qstring.h>
          #include <QtCore/qdatetime.h>
          
          QT_BEGIN_NAMESPACE
          
          class QOAuth2ImplicitFlowPrivate : public QAbstractOAuth2Private
          {
              Q_DECLARE_PUBLIC(QOAuth2ImplicitFlow)
          
          public:
              QOAuth2ImplicitFlowPrivate(const QUrl &authorizationUrl,
                                                  const QString &clientIdentifier,
                                                  QNetworkAccessManager *manager = nullptr);
          
              void _q_handleCallback(const QVariantMap &data);
              void _q_authenticate(QNetworkReply *reply, QAuthenticator *authenticator);
          
              QString tokenType;
              QPointer<QNetworkReply> currentReply;
          };
          
          QT_END_NAMESPACE
          
          #endif // QT_NO_HTTP
          
          #endif // QOAUTH2IMPLICITFLOW_P_H
          
          
          

          qoauth2implicitflow.cpp

          #ifndef QT_NO_HTTP
          
          #include <qoauth2implicitflow.h>
          #include <private/qoauth2implicitflow_p.h>
          
          #include <qmap.h>
          #include <qurl.h>
          #include <qvariant.h>
          #include <qurlquery.h>
          #include <qjsonobject.h>
          #include <qjsondocument.h>
          #include <qauthenticator.h>
          #include <qoauthhttpserverreplyhandler.h>
          
          #include <functional>
          
          QT_BEGIN_NAMESPACE
          
          /*!
              \class QOAuth2ImplicitFlow
              \inmodule QtNetworkAuth
              \ingroup oauth
              \brief The QOAuth2ImplicitFlow class provides an
              implementation of the
              \l {https://tools.ietf.org/html/rfc6749#section-4.2}
              {Implicit Grant} flow.
              \since 5.8
          
              This class implements the
              \l {https://tools.ietf.org/html/rfc6749#section-4.2}
              {Implicit Grant} flow, which is used both to obtain
              access tokens. It is a redirection-based flow so the
              user will need access to a web browser.
          */
          
          QOAuth2ImplicitFlowPrivate::QOAuth2ImplicitFlowPrivate(
                  const QUrl &authorizationUrl, const QString &clientIdentifier,
                  QNetworkAccessManager *manager) :
              QAbstractOAuth2Private(qMakePair(clientIdentifier, QString()), authorizationUrl, manager)
          {
              responseType = QStringLiteral("token");
          }
          
          void QOAuth2ImplicitFlowPrivate::_q_handleCallback(const QVariantMap &data)
          {
              Q_Q(QOAuth2ImplicitFlow);
              using Key = QAbstractOAuth2Private::OAuth2KeyString;
          
              if (status != QAbstractOAuth::Status::NotAuthenticated) {
                  qCWarning(loggingCategory, "Unexpected call");
                  return;
              }
          
              Q_ASSERT(!state.isEmpty());
          
              const QString error = data.value(Key::error).toString();
              const QString code = data.value(Key::code).toString();
              const QString receivedState = data.value(Key::state).toString();
              if (error.size()) {
                  const QString uri = data.value(Key::errorUri).toString();
                  const QString description = data.value(Key::errorDescription).toString();
                  qCWarning(loggingCategory, "AuthenticationError: %s(%s): %s",
                           qPrintable(error), qPrintable(uri), qPrintable(description));
                  Q_EMIT q->error(error, description, uri);
                  return;
              }
              if (code.isEmpty()) {
                  qCWarning(loggingCategory, "AuthenticationError: Code not received");
                  return;
              }
              if (receivedState.isEmpty()) {
                  qCWarning(loggingCategory, "State not received");
                  return;
              }
              if (state != receivedState) {
                  qCWarning(loggingCategory, "State mismatch");
                  return;
              }
          
              setStatus(QAbstractOAuth::Status::TemporaryCredentialsReceived);
          
              QVariantMap copy(data);
              copy.remove(Key::code);
              extraTokens = copy;
            //  q->requestAccessToken(code);
          }
          
          void QOAuth2ImplicitFlowPrivate::_q_accessTokenRequestFinished(const QVariantMap &values)
          {
              Q_Q(QOAuth2ImplicitFlow);
              using Key = QAbstractOAuth2Private::OAuth2KeyString;
          
              if (values.contains(Key::error)) {
                  const QString error = values.value(Key::error).toString();
                  qCWarning(loggingCategory, "Error: %s", qPrintable(error));
                  return;
              }
          
              bool ok;
              const QString accessToken = values.value(Key::accessToken).toString();
              tokenType = values.value(Key::tokenType).toString();
              int expiresIn = values.value(Key::expiresIn).toInt(&ok);
              if (!ok)
                  expiresIn = -1;
             
              scope = values.value(Key::scope).toString();
              if (accessToken.isEmpty()) {
                  qCWarning(loggingCategory, "Access token not received");
                  return;
              }
              q->setToken(accessToken);
          
              const QDateTime currentDateTime = QDateTime::currentDateTime();
              if (expiresIn > 0 && currentDateTime.secsTo(expiresAt) != expiresIn) {
                  expiresAt = currentDateTime.addSecs(expiresIn);
                  Q_EMIT q->expirationAtChanged(expiresAt);
              }
          
              setStatus(QAbstractOAuth::Status::Granted);
          }
          
          void QOAuth2ImplicitFlowPrivate::_q_authenticate(QNetworkReply *reply,
                                                                    QAuthenticator *authenticator)
          {
              if (reply == currentReply){
                  const auto url = reply->url();
                 // if (url == accessTokenUrl) {
                     qDebug() << "accessToken Artifact \n";
                      authenticator->setUser(clientIdentifier);
                      authenticator->setPassword(QString());
                //  }
              }
          }
          
          /*!
              Constructs a QOAuth2ImplicitFlow object with parent
              object \a parent.
          */
          QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(QObject *parent) :
              QOAuth2ImplicitFlow(nullptr,
                                           parent)
          {}
          
          /*!
              Constructs a QOAuth2ImplicitFlow object using \a parent
              as parent and sets \a manager as the network access manager.
          */
          QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(QNetworkAccessManager *manager,
                                                                     QObject *parent) :
              QOAuth2ImplicitFlow(QString(),
                                           manager,
                                           parent)
          {}
          
          /*!
              Constructs a QOAuth2ImplicitFlow object using \a parent
              as parent and sets \a manager as the network access manager. The
              client identifier is set to \a clientIdentifier.
          */
          QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(const QString &clientIdentifier,
                                                                     QNetworkAccessManager *manager,
                                                                     QObject *parent) :
              QAbstractOAuth2(*new QOAuth2ImplicitFlowPrivate(QUrl(), clientIdentifier,
                                                                       manager),
                              parent)
          {}
          
          /*!
              Constructs a QOAuth2ImplicitFlow object using \a parent
              as parent and sets \a manager as the network access manager. The
              authenticate URL is set to \a authenticateUrl and the access
              token URL is set to \a accessTokenUrl.
          */
          QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(const QUrl &authenticateUrl,        
                                                                     QNetworkAccessManager *manager,
                                                                     QObject *parent) :
              QAbstractOAuth2(*new QOAuth2ImplicitFlowPrivate(authenticateUrl,
                                                                       QString(), manager),
                              parent)
          {}
          
          /*!
              Constructs a QOAuth2ImplicitFlow object using \a parent
              as parent and sets \a manager as the network access manager. The
              client identifier is set to \a clientIdentifier the authenticate
              URL is set to \a authenticateUrl and the access token URL is set
              to \a accessTokenUrl.
          */
          QOAuth2ImplicitFlow::QOAuth2ImplicitFlow(const QString &clientIdentifier,
                                                                     const QUrl &authenticateUrl,
                                                                     QNetworkAccessManager *manager,
                                                                     QObject *parent) :
              QAbstractOAuth2(*new QOAuth2ImplicitFlowPrivate(authenticateUrl,
                                                                       clientIdentifier, manager),
                              parent)
          {}
          
          /*!
              Destroys the QOAuth2ImplicitFlow instance.
          */
          QOAuth2ImplicitFlow::~QOAuth2ImplicitFlow()
          {}
          
          
          /*!
              Starts the authentication flow as described in
              \l {https://tools.ietf.org/html/rfc6749#section-4.2}{The OAuth
              2.0 Authorization Framework}
          */
          void QOAuth2ImplicitFlow::grant()
          {
              Q_D(QOAuth2ImplicitFlow);
              if (d->authorizationUrl.isEmpty()) {
                  qCWarning(d->loggingCategory, "No authenticate Url set");
                  return;
              }
              resourceOwnerAuthorization(d->authorizationUrl);
          }
          
          /*!
              Generates an authentication URL to be used in the
              \l {https://tools.ietf.org/html/rfc6749#section-4.2.1}
              {Authorization Request} using \a parameters.
          */
          QUrl QOAuth2ImplicitFlow::buildAuthenticateUrl(const QVariantMap &parameters)
          {
              Q_D(QOAuth2ImplicitFlow);
              using Key = QAbstractOAuth2Private::OAuth2KeyString;
          
              if (d->state.isEmpty())
                  setState(QAbstractOAuth2Private::generateRandomState());
              Q_ASSERT(!d->state.isEmpty());
              const QString state = d->state;
          
              QVariantMap p(parameters);
              QUrl url(d->authorizationUrl);
              p.insert(Key::responseType, responseType());
              p.insert(Key::clientIdentifier, d->clientIdentifier);
              p.insert(Key::redirectUri, callback());
              p.insert(Key::scope, d->scope);
              p.insert(Key::state, state);
              if (d->modifyParametersFunction)
                  d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
              url.setQuery(d->createQuery(p));
              connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, this,
                      &QOAuth2ImplicitFlow::authorizationCallbackReceived, Qt::UniqueConnection);
              setStatus(QAbstractOAuth::Status::NotAuthenticated);
              qCDebug(d->loggingCategory, "Generated URL: %s", qPrintable(url.toString()));
              return url;
          }
          
          /*!
              Builds an authentication URL using \a url and \a parameters. This
              function emits an authorizeWithBrowser() signal to require user
              interaction.
          */
          void QOAuth2ImplicitFlow::resourceOwnerAuthorization(const QUrl &url,
                                                                        const QVariantMap &parameters)
          {
              Q_D(QOAuth2ImplicitFlow);
              if (Q_UNLIKELY(url != d->authorizationUrl)) {
                  qCWarning(d->loggingCategory, "Invalid URL: %s", qPrintable(url.toString()));
                  return;
              }
              const QUrl u = buildAuthenticateUrl(parameters);
              QObjectPrivate::connect(this, &QOAuth2ImplicitFlow::authorizationCallbackReceived, d,
                                      &QOAuth2ImplicitFlowPrivate::_q_handleCallback,
                                      Qt::UniqueConnection);
              Q_EMIT authorizeWithBrowser(u);
          }
          
          QT_END_NAMESPACE
          
          #endif // QT_NO_HTTP
          
          
          1 Reply Last reply Reply Quote 0
          • SGaist
            SGaist Lifetime Qt Champion last edited by

            Maybe a silly question but did you rebuild and install the module ?
            If so, did you add these files to the relevant .pro file ?

            Interested in AI ? www.idiap.ch
            Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

            1 Reply Last reply Reply Quote 0
            • B
              basyskom-dege last edited by

              Well i recompiled it and added all files to the relevant .pro files. The compiling output created the .o files and the compiling process looked fine. Do i have to do extra steps to install this? Qt creator finds the #include <QOAuth2ImplicitFlow> module and recognizes the functions. Just the linker does not.

              1 Reply Last reply Reply Quote 0
              • SGaist
                SGaist Lifetime Qt Champion last edited by

                But did you install the module after rebuilding it ?

                Interested in AI ? www.idiap.ch
                Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                1 Reply Last reply Reply Quote 1
                • B
                  basyskom-dege last edited by

                  Thats it! Forgot the make install :( Well thanks for your help! It works now. The test runs and the linker errors are gone. Once it works as intendet (the implicit flow) I might do a pull request since I want to use it in the OpenAPI Code-Generator for Qt5. Thanks again!

                  1 Reply Last reply Reply Quote 0
                  • SGaist
                    SGaist Lifetime Qt Champion last edited by

                    Great !

                    The patch submission is a really good idea. Do not hesitate :-)

                    Interested in AI ? www.idiap.ch
                    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

                    1 Reply Last reply Reply Quote 0
                    • First post
                      Last post