diff options
| -rw-r--r-- | examples/oauth/redditclient/redditmodel.cpp | 2 | ||||
| -rw-r--r-- | src/oauth/doc/snippets/src_oauth_replyhandlers.cpp | 4 | ||||
| -rw-r--r-- | src/oauth/qabstractoauth2.cpp | 38 | ||||
| -rw-r--r-- | src/oauth/qabstractoauth2.h | 15 | ||||
| -rw-r--r-- | src/oauth/qabstractoauth2_p.h | 8 | ||||
| -rw-r--r-- | src/oauth/qoauth2authorizationcodeflow.cpp | 2 | ||||
| -rw-r--r-- | src/oauth/qoauth2deviceauthorizationflow.cpp | 2 | ||||
| -rw-r--r-- | tests/auto/oauth2/tst_oauth2.cpp | 116 | ||||
| -rw-r--r-- | tests/auto/oauth2deviceflow/tst_oauth2deviceflow.cpp | 72 |
9 files changed, 147 insertions, 112 deletions
diff --git a/examples/oauth/redditclient/redditmodel.cpp b/examples/oauth/redditclient/redditmodel.cpp index ac7ea30..c50e0c7 100644 --- a/examples/oauth/redditclient/redditmodel.cpp +++ b/examples/oauth/redditclient/redditmodel.cpp @@ -33,7 +33,7 @@ RedditModel::RedditModel(const QString &clientId, QObject *parent) : oauth2.setReplyHandler(replyHandler); oauth2.setAuthorizationUrl(QUrl(authorizationUrl)); oauth2.setTokenUrl(QUrl(accessTokenUrl)); - oauth2.setRequestedScopeTokens({"identity"_L1, "read"_L1}); + oauth2.setRequestedScopeTokens({"identity", "read"}); oauth2.setClientIdentifier(clientId); QObject::connect(&oauth2, &QAbstractOAuth::granted, this, [this] { diff --git a/src/oauth/doc/snippets/src_oauth_replyhandlers.cpp b/src/oauth/doc/snippets/src_oauth_replyhandlers.cpp index 7dff697..76dbcaa 100644 --- a/src/oauth/doc/snippets/src_oauth_replyhandlers.cpp +++ b/src/oauth/doc/snippets/src_oauth_replyhandlers.cpp @@ -43,7 +43,7 @@ using namespace Qt::StringLiterals; static constexpr auto authorizationUrl = "https://www.myqtapp.example.com/api/v1/authorize"_L1; static constexpr auto accessTokenUrl = "https://www.myqtapp.example.com/api/v1/access_token"_L1; static constexpr auto clientIdentifier = "some_client_id"_L1; -static constexpr auto scope = "read"_L1; +static constexpr auto scope = "read"; static constexpr auto oidcConfigUrl = "https://www.myqtapp.example.com/.well-known/openid-configuration"_L1; static constexpr auto oidcJwksUrl = "https://www.myqtapp.example.com/v1/certs"_L1; @@ -62,7 +62,7 @@ HttpExample::HttpExample() #endif //! [oidc-setting-scope] - m_oauth.setRequestedScopeTokens({"openid"_L1}); + m_oauth.setRequestedScopeTokens({"openid"}); //! [oidc-setting-scope] //! [httpserver-service-configuration] diff --git a/src/oauth/qabstractoauth2.cpp b/src/oauth/qabstractoauth2.cpp index 3b40d90..e0250be 100644 --- a/src/oauth/qabstractoauth2.cpp +++ b/src/oauth/qabstractoauth2.cpp @@ -439,7 +439,7 @@ void QAbstractOAuth2Private::setExpiresAt(const QDateTime &expiration) emit q->expirationAtChanged(expiresAtUtc.toLocalTime()); } -void QAbstractOAuth2Private::setGrantedScopeTokens(const QStringList &tokens) +void QAbstractOAuth2Private::setGrantedScopeTokens(const QSet<QByteArray> &tokens) { if (tokens == grantedScopeTokens) return; @@ -448,6 +448,26 @@ void QAbstractOAuth2Private::setGrantedScopeTokens(const QStringList &tokens) Q_EMIT q->grantedScopeTokensChanged(grantedScopeTokens); } +QByteArray QAbstractOAuth2Private::joinedScope(const QSet<QByteArray> &scopeTokens) +{ + QByteArray joined; + const char *separator = ""; + for (const auto &token : scopeTokens) { + joined += separator; + joined += token; + separator = " "; + } + return joined; +} + +QSet<QByteArray> QAbstractOAuth2Private::splitScope(QStringView scope) +{ + QSet<QByteArray> split; + for (const auto &token : scope.split(u' ', Qt::SkipEmptyParts)) + split.insert(token.toUtf8()); + return split; +} + QString QAbstractOAuth2Private::generateRandomState() { return QString::fromLatin1(QAbstractOAuthPrivate::generateRandomBase64String(8)); @@ -549,7 +569,7 @@ bool QAbstractOAuth2Private::authorizationShouldIncludeNonce() const case QAbstractOAuth2::NonceMode::Disabled: return false; case QAbstractOAuth2::NonceMode::Automatic: - return requestedScopeTokens.contains("openid"_L1); + return requestedScopeTokens.contains("openid"); }; return false; } @@ -614,7 +634,7 @@ void QAbstractOAuth2Private::_q_tokenRequestFinished(const QVariantMap &values) // Note: 'scope' variable has two roles: requested scope, and later granted scope. // Therefore 'scope' needs to be set if the granted scope differs from 'scope'. const QString receivedGrantedScope = values.value(QtOAuth2RfcKeywords::scope).toString(); - const QStringList splitGrantedScope = receivedGrantedScope.split(" "_L1, Qt::SkipEmptyParts); + const QSet<QByteArray> splitGrantedScope = splitScope(receivedGrantedScope); if (splitGrantedScope.isEmpty()) { setGrantedScopeTokens(requestedScopeTokens); } else { @@ -631,7 +651,7 @@ void QAbstractOAuth2Private::_q_tokenRequestFinished(const QVariantMap &values) // https://openid.net/specs/openid-connect-core-1_0-final.html#AuthRequest (cf. 'scope') // https://openid.net/specs/openid-connect-core-1_0-final.html#TokenResponse const QString receivedIdToken = values.value(QtOAuth2RfcKeywords::idToken).toString(); - if (grantedScopeTokens.contains("openid"_L1) && receivedIdToken.isEmpty()) { + if (grantedScopeTokens.contains("openid") && receivedIdToken.isEmpty()) { setIdToken({}); _q_tokenRequestFailed(QAbstractOAuth::Error::OAuthTokenNotFoundError, "ID token not received"_L1); @@ -1100,7 +1120,7 @@ QString QAbstractOAuth2::scope() const } #endif -QStringList QAbstractOAuth2::grantedScopeTokens() const +QSet<QByteArray> QAbstractOAuth2::grantedScopeTokens() const { Q_D(const QAbstractOAuth2); return d->grantedScopeTokens; @@ -1114,7 +1134,7 @@ void QAbstractOAuth2::setScope(const QString &scope) d->scope = scope; QT_IGNORE_DEPRECATIONS(Q_EMIT scopeChanged(scope);) } - QStringList splitScope = scope.split(" "_L1, Qt::SkipEmptyParts); + const QSet<QByteArray> splitScope = d->splitScope(scope); if (d->requestedScopeTokens != splitScope) { d->requestedScopeTokens = splitScope; Q_EMIT requestedScopeTokensChanged(splitScope); @@ -1122,13 +1142,13 @@ void QAbstractOAuth2::setScope(const QString &scope) } #endif -QStringList QAbstractOAuth2::requestedScopeTokens() const +QSet<QByteArray> QAbstractOAuth2::requestedScopeTokens() const { Q_D(const QAbstractOAuth2); return d->requestedScopeTokens; } -void QAbstractOAuth2::setRequestedScopeTokens(const QStringList &tokens) +void QAbstractOAuth2::setRequestedScopeTokens(const QSet<QByteArray> &tokens) { Q_D(QAbstractOAuth2); if (tokens != d->requestedScopeTokens) { @@ -1136,7 +1156,7 @@ void QAbstractOAuth2::setRequestedScopeTokens(const QStringList &tokens) Q_EMIT requestedScopeTokensChanged(tokens); } #if QT_REMOVAL_QT7_DEPRECATED_SINCE(6, 13) - QString joinedScope = tokens.join(" "_L1); + QString joinedScope = QString::fromLatin1(d->joinedScope(tokens)); if (joinedScope != d->scope) { d->scope = joinedScope; QT_IGNORE_DEPRECATIONS(Q_EMIT scopeChanged(joinedScope);) diff --git a/src/oauth/qabstractoauth2.h b/src/oauth/qabstractoauth2.h index 4fe783f..f322f5a 100644 --- a/src/oauth/qabstractoauth2.h +++ b/src/oauth/qabstractoauth2.h @@ -8,6 +8,7 @@ #ifndef QT_NO_HTTP +#include <QtCore/qcontainerfwd.h> #include <QtCore/qdatetime.h> #include <QtNetworkAuth/qabstractoauth.h> @@ -23,9 +24,9 @@ class Q_OAUTH_EXPORT QAbstractOAuth2 : public QAbstractOAuth #if QT_REMOVAL_QT7_DEPRECATED_SINCE(6, 13) Q_PROPERTY(QString scope READ scope WRITE setScope NOTIFY scopeChanged) #endif - Q_PROPERTY(QStringList grantedScopeTokens + Q_PROPERTY(QSet<QByteArray> grantedScopeTokens READ grantedScopeTokens NOTIFY grantedScopeTokensChanged) - Q_PROPERTY(QStringList requestedScopeTokens + Q_PROPERTY(QSet<QByteArray> requestedScopeTokens READ requestedScopeTokens WRITE setRequestedScopeTokens NOTIFY requestedScopeTokensChanged) @@ -122,10 +123,10 @@ public: void setScope(const QString &scope); #endif - QStringList grantedScopeTokens() const; + QSet<QByteArray> grantedScopeTokens() const; - QStringList requestedScopeTokens() const; - void setRequestedScopeTokens(const QStringList &tokens); + QSet<QByteArray> requestedScopeTokens() const; + void setRequestedScopeTokens(const QSet<QByteArray> &tokens); QString userAgent() const; void setUserAgent(const QString &userAgent); @@ -190,8 +191,8 @@ Q_SIGNALS: QT_DEPRECATED_VERSION_X_6_13("Use requestedScopeTokens and grantedScopeTokens properties instead.") void scopeChanged(const QString &scope); #endif - void grantedScopeTokensChanged(const QStringList &tokens); - void requestedScopeTokensChanged(const QStringList &tokens); + void grantedScopeTokensChanged(const QSet<QByteArray> &tokens); + void requestedScopeTokensChanged(const QSet<QByteArray> &tokens); void userAgentChanged(const QString &userAgent); void responseTypeChanged(const QString &responseType); void clientIdentifierSharedKeyChanged(const QString &clientIdentifierSharedKey); diff --git a/src/oauth/qabstractoauth2_p.h b/src/oauth/qabstractoauth2_p.h index 00b559f..51ccd73 100644 --- a/src/oauth/qabstractoauth2_p.h +++ b/src/oauth/qabstractoauth2_p.h @@ -46,7 +46,9 @@ public: ~QAbstractOAuth2Private(); void setExpiresAt(const QDateTime &expiration); - void setGrantedScopeTokens(const QStringList &tokens); + void setGrantedScopeTokens(const QSet<QByteArray> &tokens); + static QByteArray joinedScope(const QSet<QByteArray> &scopeTokens); + static QSet<QByteArray> splitScope(QStringView scope); static QString generateRandomState(); static QString generateNonce(); QNetworkRequest createRequest(QUrl url, const QVariantMap *parameters = nullptr); @@ -84,8 +86,8 @@ public: #if QT_REMOVAL_QT7_DEPRECATED_SINCE(6, 13) QString scope; #endif - QStringList requestedScopeTokens; - QStringList grantedScopeTokens; + QSet<QByteArray> requestedScopeTokens; + QSet<QByteArray> grantedScopeTokens; QString state = generateRandomState(); QString userAgent = QStringLiteral("QtOAuth/1.0 (+https://www.qt.io)"); QString responseType; diff --git a/src/oauth/qoauth2authorizationcodeflow.cpp b/src/oauth/qoauth2authorizationcodeflow.cpp index 3cf61ed..33c47fa 100644 --- a/src/oauth/qoauth2authorizationcodeflow.cpp +++ b/src/oauth/qoauth2authorizationcodeflow.cpp @@ -457,7 +457,7 @@ QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QMultiMap<QString, p.insert(QtOAuth2RfcKeywords::clientIdentifier, d->clientIdentifier); p.insert(QtOAuth2RfcKeywords::redirectUri, callback()); if (!d->requestedScopeTokens.isEmpty()) - p.insert(QtOAuth2RfcKeywords::scope, d->requestedScopeTokens.join(" "_L1)); + p.insert(QtOAuth2RfcKeywords::scope, d->joinedScope(d->requestedScopeTokens)); p.insert(QtOAuth2RfcKeywords::state, toUrlFormEncoding(state)); if (d->pkceMethod != PkceMethod::None) { p.insert(QtOAuth2RfcKeywords::codeChallenge, d->createPKCEChallenge()); diff --git a/src/oauth/qoauth2deviceauthorizationflow.cpp b/src/oauth/qoauth2deviceauthorizationflow.cpp index 0afb354..6ac27b9 100644 --- a/src/oauth/qoauth2deviceauthorizationflow.cpp +++ b/src/oauth/qoauth2deviceauthorizationflow.cpp @@ -658,7 +658,7 @@ void QOAuth2DeviceAuthorizationFlow::grant() QMultiMap<QString, QVariant> parameters; parameters.insert(QtOAuth2RfcKeywords::clientIdentifier, d->clientIdentifier); if (!d->requestedScopeTokens.isEmpty()) - parameters.insert(QtOAuth2RfcKeywords::scope, d->requestedScopeTokens.join(" "_L1)); + parameters.insert(QtOAuth2RfcKeywords::scope, d->joinedScope(d->requestedScopeTokens)); if (d->authorizationShouldIncludeNonce()) { if (d->nonce.isEmpty()) setNonce(QAbstractOAuth2Private::generateNonce()); diff --git a/tests/auto/oauth2/tst_oauth2.cpp b/tests/auto/oauth2/tst_oauth2.cpp index 5999805..6d8a2a0 100644 --- a/tests/auto/oauth2/tst_oauth2.cpp +++ b/tests/auto/oauth2/tst_oauth2.cpp @@ -860,7 +860,7 @@ void tst_OAuth2::nonce() // Verify that nonce is set to authorization request when appropriate oauth2.setNonce(nonce); - oauth2.setRequestedScopeTokens({u"scope_item1"_s}); + oauth2.setRequestedScopeTokens({"scope_item1"}); // -- Nonce is always included oauth2.setNonceMode(QAbstractOAuth2::NonceMode::Enabled); @@ -877,7 +877,7 @@ void tst_OAuth2::nonce() oauth2.grant(); QVERIFY(nonceInAuthorizationUrl.isEmpty()); - oauth2.setRequestedScopeTokens({u"scope_item1"_s, u"openid"_s}); + oauth2.setRequestedScopeTokens({"scope_item1", "openid"}); oauth2.grant(); QCOMPARE(nonceInAuthorizationUrl, nonce); @@ -892,7 +892,7 @@ void tst_OAuth2::nonce() void tst_OAuth2::idToken() { QOAuth2AuthorizationCodeFlow oauth2; - oauth2.setRequestedScopeTokens({"openid"_L1}); + oauth2.setRequestedScopeTokens({"openid"}); oauth2.setAuthorizationUrl({"authorizationUrl"_L1}); oauth2.setTokenUrl({"accessTokenUrl"_L1}); oauth2.setState("a_state"_L1); @@ -905,7 +905,7 @@ void tst_OAuth2::idToken() QVERIFY(oauth2.idToken().isEmpty()); // Test without openid and verify idToken doesn't change - oauth2.setRequestedScopeTokens({"read"_L1}); + oauth2.setRequestedScopeTokens({"read"}); oauth2.grant(); // Conclude authorization stage in order to proceed to access token stage replyHandler.emitCallbackReceived({{"code"_L1, "acode"_L1}, {"state"_L1, "a_state"_L1}}); @@ -919,7 +919,7 @@ void tst_OAuth2::idToken() // Note: using a proper JWT or setting the matching 'nonce' is not required for this tests // purpose as we don't currently validate the received token, but no harm in being thorough auto idToken = createSignedJWT({}, {{"nonce"_L1, oauth2.nonce()}}); - oauth2.setRequestedScopeTokens({"openid"_L1}); + oauth2.setRequestedScopeTokens({"openid"}); oauth2.grant(); replyHandler.emitCallbackReceived({{"code"_L1, "acode"_L1}, {"state"_L1, "a_state"_L1}}); replyHandler.emitTokensReceived({{"access_token"_L1, "at"_L1}, {"id_token"_L1, idToken}}); @@ -1024,25 +1024,25 @@ void tst_OAuth2::scope() void tst_OAuth2::scopeAndRequestedScope_data() { - const QString f = u"first"_s; - const QString s = u"second"_s; - const QString fs = u"first second"_s; + const QByteArray f = "first"; + const QByteArray s = "second"; + const QByteArray fs = "first second"; - QTest::addColumn<QString>("scope"); - QTest::addColumn<QString>("expected_scope"); - QTest::addColumn<QStringList>("requested_scope"); - QTest::addColumn<QString>("expected_resulting_request_scope"); + QTest::addColumn<QByteArray>("scope"); + QTest::addColumn<QByteArray>("expected_scope"); + QTest::addColumn<QSet<QByteArray>>("requested_scope"); + QTest::addColumn<QByteArray>("expected_resulting_request_scope"); - QTest::addRow("singlescope") << f << f << QStringList{f} << f; - QTest::addRow("multiscope") << fs << fs << QStringList{f, s} << fs; + QTest::addRow("singlescope") << f << f << QSet{f} << f; + QTest::addRow("multiscope") << fs << fs << QSet{f, s} << fs; } void tst_OAuth2::scopeAndRequestedScope() { - QFETCH(QString, scope); - QFETCH(QString, expected_scope); - QFETCH(QStringList, requested_scope); - QFETCH(QString, expected_resulting_request_scope); + QFETCH(QByteArray, scope); + QFETCH(QByteArray, expected_scope); + QFETCH(QSet<QByteArray>, requested_scope); + QFETCH(QByteArray, expected_resulting_request_scope); QOAuth2AuthorizationCodeFlow oauth2; oauth2.setAuthorizationUrl({"authorizationUrl"_L1}); @@ -1068,10 +1068,13 @@ void tst_OAuth2::scopeAndRequestedScope() QCOMPARE(requestedScopeTokensSpy.size(), 1); QCOMPARE(oauth2.requestedScopeTokens(), requested_scope); - QCOMPARE(requestedScopeTokensSpy.at(0).at(0).toStringList(), requested_scope); + QCOMPARE(requestedScopeTokensSpy.at(0).at(0).value<QSet<QByteArray>>(), requested_scope); oauth2.grant(); - QCOMPARE(resultingRequestScope, expected_resulting_request_scope); + auto scopeTokens = resultingRequestScope.toLatin1().split(' '); + QCOMPARE_EQ(scopeTokens.size(), requested_scope.size()); + for (const auto &token : scopeTokens) + QVERIFY2(requested_scope.contains(token), token.data()); // Clear data oauth2.setScope(u""_s); @@ -1085,37 +1088,40 @@ void tst_OAuth2::scopeAndRequestedScope() QCOMPARE(requestedScopeTokensSpy.size(), 1); QCOMPARE(oauth2.requestedScopeTokens(), requested_scope); - QCOMPARE(requestedScopeTokensSpy.at(0).at(0).toStringList(), requested_scope); + QCOMPARE(requestedScopeTokensSpy.at(0).at(0).value<QSet<QByteArray>>(), requested_scope); QCOMPARE(scopeSpy.size(), 1); - QCOMPARE(oauth2.scope(), expected_scope); - QCOMPARE(scopeSpy.at(0).at(0).toString(), expected_scope); + scopeTokens = oauth2.scope().toLatin1().split(' '); + QCOMPARE_EQ(scopeTokens.size(), requested_scope.size()); + for (const auto &token : scopeTokens) + QVERIFY2(requested_scope.contains(token), token.data()); oauth2.grant(); - QCOMPARE(resultingRequestScope, expected_resulting_request_scope); + + scopeTokens = resultingRequestScope.toLatin1().split(' '); + QCOMPARE_EQ(scopeTokens.size(), requested_scope.size()); + for (const auto &token : scopeTokens) + QVERIFY2(requested_scope.contains(token), token.data()); } QT_WARNING_POP #endif // QT_REMOVAL_QT7_DEPRECATED_SINCE(6, 13) void tst_OAuth2::requestedScopeTokens_data() { - const QString f = u"first"_s; - const QString s = u"second"_s; - const QString fs = u"first second"_s; + const QByteArray f = "first"; + const QByteArray s = "second"; - QTest::addColumn<QStringList>("requested_scope"); - QTest::addColumn<QStringList>("expected_requested_scope"); - QTest::addColumn<QString>("expected_resulting_request_scope"); + QTest::addColumn<QSet<QByteArray>>("requested_scope"); + QTest::addColumn<QSet<QByteArray>>("expected_requested_scope"); - QTest::addRow("singlescope") << QStringList{f} << QStringList{f} << f; - QTest::addRow("multiscope") << QStringList{f, s} << QStringList{f, s} << fs; + QTest::addRow("singlescope") << QSet{f} << QSet{f}; + QTest::addRow("multiscope") << QSet{f, s} << QSet{f, s}; } void tst_OAuth2::requestedScopeTokens() { - QFETCH(QStringList, requested_scope); - QFETCH(QStringList, expected_requested_scope); - QFETCH(QString, expected_resulting_request_scope); + QFETCH(QSet<QByteArray>, requested_scope); + QFETCH(QSet<QByteArray>, expected_requested_scope); QOAuth2AuthorizationCodeFlow oauth2; oauth2.setAuthorizationUrl({"authorizationUrl"_L1}); @@ -1134,43 +1140,47 @@ void tst_OAuth2::requestedScopeTokens() QCOMPARE(requestedScopeTokensSpy.size(), 1); QCOMPARE(oauth2.requestedScopeTokens(), expected_requested_scope); - QCOMPARE(requestedScopeTokensSpy.at(0).at(0).toStringList(), expected_requested_scope); + QCOMPARE(requestedScopeTokensSpy.at(0).at(0).value<QSet<QByteArray>>(), + expected_requested_scope); oauth2.grant(); - QCOMPARE(resultingRequestScope, expected_resulting_request_scope); + const auto scopeTokens = resultingRequestScope.toLatin1().split(' '); + QCOMPARE_EQ(scopeTokens.size(), requested_scope.size()); + for (const auto &token : scopeTokens) + QVERIFY2(requested_scope.contains(token), token.data()); } void tst_OAuth2::grantedScopeTokens_data() { - const QStringList requestedScopeTokens = {u"first"_s, u"second"_s}; - const QString scope = u"first second"_s; - const QString granted1 = u"granted1"_s; - const QString granted2 = u"granted2"_s; - const QString grantedJoined = granted1 + u" "_s + granted2; - const QStringList grantedList = {granted1, granted2}; - - QTest::addColumn<QStringList>("requested_scope"); - QTest::addColumn<QString>("granted_scope"); - QTest::addColumn<QStringList>("expected_granted_scope"); + const QSet<QByteArray> requestedScopeTokens = {"first", "second"}; + const QByteArray scope = "first second"; + const QByteArray granted1 = "granted1"; + const QByteArray granted2 = "granted2"; + const QByteArray grantedJoined = granted1 + " " + granted2; + const QSet<QByteArray> grantedList = {granted1, granted2}; + + QTest::addColumn<QSet<QByteArray>>("requested_scope"); + QTest::addColumn<QByteArray>("granted_scope"); + QTest::addColumn<QSet<QByteArray>>("expected_granted_scope"); QTest::addRow("requested_scope_returned") << requestedScopeTokens << scope << requestedScopeTokens; QTest::addRow("differing_singlescope_returned") - << requestedScopeTokens << granted1 << QStringList{granted1}; + << requestedScopeTokens << granted1 << QSet{granted1}; QTest::addRow("differing_multiscope_returned") << requestedScopeTokens << grantedJoined << grantedList; QTest::addRow("empty_scope_returned") - << requestedScopeTokens << u""_s << requestedScopeTokens; + << requestedScopeTokens << ""_ba << requestedScopeTokens; } void tst_OAuth2::grantedScopeTokens() { - QFETCH(QStringList, requested_scope); - QFETCH(QString, granted_scope); - QFETCH(QStringList, expected_granted_scope); + QFETCH(QSet<QByteArray>, requested_scope); + QFETCH(QByteArray, granted_scope); + QFETCH(QSet<QByteArray>, expected_granted_scope); QOAuth2AuthorizationCodeFlow oauth2; QSignalSpy grantedSpy(&oauth2, &QAbstractOAuth2::grantedScopeTokensChanged); @@ -1195,7 +1205,7 @@ void tst_OAuth2::grantedScopeTokens() QTRY_COMPARE(grantedSpy.size(), 1); QCOMPARE(oauth2.grantedScopeTokens(), expected_granted_scope); - QCOMPARE(grantedSpy.at(0).at(0).toStringList(), expected_granted_scope); + QCOMPARE(grantedSpy.at(0).at(0).value<QSet<QByteArray>>(), expected_granted_scope); } void tst_OAuth2::setAutoRefresh() diff --git a/tests/auto/oauth2deviceflow/tst_oauth2deviceflow.cpp b/tests/auto/oauth2deviceflow/tst_oauth2deviceflow.cpp index 2ba1676..8aec9cc 100644 --- a/tests/auto/oauth2deviceflow/tst_oauth2deviceflow.cpp +++ b/tests/auto/oauth2deviceflow/tst_oauth2deviceflow.cpp @@ -1,6 +1,7 @@ // Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only +#include <QtTest/qtestcase.h> #include <QtTest> #include "oauthtestutils.h" @@ -501,7 +502,7 @@ void tst_OAuth2DeviceFlow::startStopTokenPolling() void tst_OAuth2DeviceFlow::getAndRefreshToken() { static constexpr auto clientId = "a-client-id"_L1; - static constexpr auto scope = "a-scope"_L1; + static constexpr auto scope = "a-scope"; static constexpr auto accessToken = "an-access-token"_L1; static constexpr auto clientSecret = "a-client-secret"_L1; static constexpr auto deviceCode = "a-device-code"_L1; @@ -1012,7 +1013,7 @@ void tst_OAuth2DeviceFlow::nonce() authBody, authHttpStatus, tokenBody, tokenHttpStatus)); QOAuth2DeviceAuthorizationFlow oauth2; - oauth2.setRequestedScopeTokens({"openid"_L1}); + oauth2.setRequestedScopeTokens({"openid"}); oauth2.setAuthorizationUrl(authorizationServer->url("authorizationEndpoint"_L1)); oauth2.setTokenUrl(authorizationServer->url("tokenEndpoint"_L1)); @@ -1047,7 +1048,7 @@ void tst_OAuth2DeviceFlow::nonce() // Verify that nonce is set to authorization request when appropriate oauth2.setNonce(nonce); - oauth2.setRequestedScopeTokens({u"scope_item1"_s}); + oauth2.setRequestedScopeTokens({"scope_item1"}); // -- Nonce is always included oauth2.setNonceMode(QAbstractOAuth2::NonceMode::Enabled); @@ -1073,7 +1074,7 @@ void tst_OAuth2DeviceFlow::nonce() parameters.setQuery(receivedAuthorizationRequests.at(0).body); QVERIFY(parameters.queryItemValue(u"nonce"_s).toUtf8().isEmpty()); - oauth2.setRequestedScopeTokens({u"scope_item1"_s, u"openid"_s}); + oauth2.setRequestedScopeTokens({"scope_item1", "openid"}); receivedAuthorizationRequests.clear(); oauth2.grant(); QTRY_COMPARE(receivedAuthorizationRequests.size(), 1); @@ -1102,7 +1103,7 @@ void tst_OAuth2DeviceFlow::idToken() DeviceFlow oauth2; oauth2.flowPrivate()->useAutoTestDurations = true; - oauth2.setRequestedScopeTokens({"openid"_L1}); + oauth2.setRequestedScopeTokens({"openid"}); oauth2.setAuthorizationUrl(authorizationServer->url("authorizationEndpoint"_L1)); oauth2.setTokenUrl(authorizationServer->url("tokenEndpoint"_L1)); @@ -1113,7 +1114,7 @@ void tst_OAuth2DeviceFlow::idToken() QVERIFY(oauth2.idToken().isEmpty()); // Test without openid and verify idToken doesn't change - oauth2.setRequestedScopeTokens({"read"_L1}); + oauth2.setRequestedScopeTokens({"read"}); oauth2.grant(); QTRY_COMPARE(oauth2.status(), Status::Granted); QVERIFY(idTokenSpy.isEmpty()); @@ -1123,7 +1124,7 @@ void tst_OAuth2DeviceFlow::idToken() // Note: using a proper JWT or setting the matching 'nonce' is not required for this tests // purpose as we don't currently validate the received token, but no harm in being thorough auto idToken = createSignedJWT({}, {{"nonce"_L1, oauth2.nonce()}}); - oauth2.setRequestedScopeTokens({"openid"_L1}); + oauth2.setRequestedScopeTokens({"openid"}); tokenBody = R"( { "access_token": "an-access-token", @@ -1154,23 +1155,20 @@ void tst_OAuth2DeviceFlow::idToken() void tst_OAuth2DeviceFlow::requestedScopeTokens_data() { - const QString f = u"first"_s; - const QString s = u"second"_s; - const QString fs = u"first second"_s; + const QByteArray f = "first"; + const QByteArray s = "second"; - QTest::addColumn<QStringList>("requested_scope"); - QTest::addColumn<QStringList>("expected_requested_scope"); - QTest::addColumn<QString>("expected_resulting_request_scope"); + QTest::addColumn<QSet<QByteArray>>("requested_scope"); + QTest::addColumn<QSet<QByteArray>>("expected_requested_scope"); - QTest::addRow("singlescope") << QStringList{f} << QStringList{f} << f; - QTest::addRow("multiscope") << QStringList{f, s} << QStringList{f, s} << fs; + QTest::addRow("singlescope") << QSet{f} << QSet{f}; + QTest::addRow("multiscope") << QSet{f, s} << QSet{f, s}; } void tst_OAuth2DeviceFlow::requestedScopeTokens() { - QFETCH(QStringList, requested_scope); - QFETCH(QStringList, expected_requested_scope); - QFETCH(QString, expected_resulting_request_scope); + QFETCH(QSet<QByteArray>, requested_scope); + QFETCH(QSet<QByteArray>, expected_requested_scope); const QString authBody = Responses::authorizationSuccess; const QString authHttpStatus = Responses::OK_200; @@ -1189,45 +1187,49 @@ void tst_OAuth2DeviceFlow::requestedScopeTokens() QCOMPARE(requestedScopeTokensSpy.size(), 1); QCOMPARE(oauth2.requestedScopeTokens(), expected_requested_scope); - QCOMPARE(requestedScopeTokensSpy.at(0).at(0).toStringList(), expected_requested_scope); + QCOMPARE(requestedScopeTokensSpy.at(0).at(0).value<QSet<QByteArray>>(), + expected_requested_scope); oauth2.grant(); QTRY_COMPARE(receivedAuthorizationRequests.size(), 1); QUrlQuery parameters(receivedAuthorizationRequests.at(0).body); - QCOMPARE(parameters.queryItemValue(u"scope"_s), expected_resulting_request_scope); + const auto scopeTokens = parameters.queryItemValue(u"scope"_s).toLatin1().split(' '); + QCOMPARE_EQ(scopeTokens.size(), requested_scope.size()); + for (const auto &token : scopeTokens) + QVERIFY2(requested_scope.contains(token), token.data()); } void tst_OAuth2DeviceFlow::grantedScopeTokens_data() { - const QStringList requestedScopeTokens = {u"first"_s, u"second"_s}; - const QString scope = u"first second"_s; - const QString granted1 = u"granted1"_s; - const QString granted2 = u"granted2"_s; - const QString grantedJoined = granted1 + u" "_s + granted2; - const QStringList grantedList = {granted1, granted2}; + const QSet<QByteArray> requestedScopeTokens = {"first", "second"}; + const QByteArray scope = "first second"; + const QByteArray granted1 = "granted1"; + const QByteArray granted2 = "granted2"; + const QByteArray grantedJoined = granted1 + " " + granted2; + const QSet<QByteArray> grantedList = {granted1, granted2}; - QTest::addColumn<QStringList>("requested_scope"); - QTest::addColumn<QString>("granted_scope"); - QTest::addColumn<QStringList>("expected_granted_scope"); + QTest::addColumn<QSet<QByteArray>>("requested_scope"); + QTest::addColumn<QByteArray>("granted_scope"); + QTest::addColumn<QSet<QByteArray>>("expected_granted_scope"); QTest::addRow("requested_scope_returned") << requestedScopeTokens << scope << requestedScopeTokens; QTest::addRow("differing_singlescope_returned") - << requestedScopeTokens << granted1 << QStringList{granted1}; + << requestedScopeTokens << granted1 << QSet<QByteArray>{granted1}; QTest::addRow("differing_multiscope_returned") << requestedScopeTokens << grantedJoined << grantedList; QTest::addRow("empty_scope_returned") - << requestedScopeTokens << u""_s << requestedScopeTokens; + << requestedScopeTokens << ""_ba << requestedScopeTokens; } void tst_OAuth2DeviceFlow::grantedScopeTokens() { - QFETCH(QStringList, requested_scope); - QFETCH(QString, granted_scope); - QFETCH(QStringList, expected_granted_scope); + QFETCH(QSet<QByteArray>, requested_scope); + QFETCH(QByteArray, granted_scope); + QFETCH(QSet<QByteArray>, expected_granted_scope); const QString authBody = Responses::authorizationSuccess; const QString authHttpStatus = Responses::OK_200; @@ -1258,7 +1260,7 @@ void tst_OAuth2DeviceFlow::grantedScopeTokens() QTRY_COMPARE(grantedSpy.size(), 1); QCOMPARE(oauth2.grantedScopeTokens(), expected_granted_scope); - QCOMPARE(grantedSpy.at(0).at(0).toStringList(), expected_granted_scope); + QCOMPARE(grantedSpy.at(0).at(0).value<QSet<QByteArray>>(), expected_granted_scope); } void tst_OAuth2DeviceFlow::refreshLeadTime_data() |
