1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29#include <QtTest>
30
31#include <QtNetworkAuth/qabstractoauthreplyhandler.h>
32#include <QtNetworkAuth/qoauth2authorizationcodeflow.h>
33
34#include "webserver.h"
35
36class tst_OAuth2 : public QObject
37{
38 Q_OBJECT
39
40private Q_SLOTS:
41 void getToken();
42 void refreshToken();
43 void getAndRefreshToken();
44 void prepareRequest();
45};
46
47struct ReplyHandler : QAbstractOAuthReplyHandler
48{
49 QString callback() const override
50 {
51 return QLatin1String("test");
52 }
53
54 void networkReplyFinished(QNetworkReply *reply) override
55 {
56 QVariantMap data;
57 const auto items = QUrlQuery(reply->readAll()).queryItems();
58 for (const auto &pair : items)
59 data.insert(key: pair.first, value: pair.second);
60 Q_EMIT tokensReceived(tokens: data);
61 }
62
63 void emitCallbackReceived(const QVariantMap &data)
64 {
65 Q_EMIT callbackReceived(values: data);
66 }
67};
68
69void tst_OAuth2::getToken()
70{
71 WebServer webServer([](const WebServer::HttpRequest &request, QTcpSocket *socket) {
72 if (request.url.path() == QLatin1String("/accessToken")) {
73 const QString text = "access_token=token&token_type=bearer";
74 const QByteArray replyMessage {
75 "HTTP/1.0 200 OK\r\n"
76 "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
77 "Content-Length: " + QByteArray::number(text.size()) + "\r\n\r\n"
78 + text.toUtf8()
79 };
80 socket->write(data: replyMessage);
81 }
82 });
83 QOAuth2AuthorizationCodeFlow oauth2;
84 oauth2.setAuthorizationUrl(webServer.url(path: QLatin1String("authorization")));
85 oauth2.setAccessTokenUrl(webServer.url(path: QLatin1String("accessToken")));
86 ReplyHandler replyHandler;
87 oauth2.setReplyHandler(&replyHandler);
88 connect(sender: &oauth2, signal: &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, slot: [&](const QUrl &url) {
89 const QUrlQuery query(url.query());
90 replyHandler.emitCallbackReceived(data: QVariantMap {
91 { QLatin1String("code"), QLatin1String("test") },
92 { QLatin1String("state"),
93 query.queryItemValue(key: QLatin1String("state")) }
94 });
95 });
96 QSignalSpy grantedSpy(&oauth2, &QOAuth2AuthorizationCodeFlow::granted);
97 oauth2.grant();
98 QTRY_COMPARE(grantedSpy.count(), 1);
99 QCOMPARE(oauth2.token(), QLatin1String("token"));
100}
101
102void tst_OAuth2::refreshToken()
103{
104 WebServer webServer([](const WebServer::HttpRequest &request, QTcpSocket *socket) {
105 if (request.url.path() == QLatin1String("/accessToken")) {
106 const QString text = "access_token=token&token_type=bearer";
107 const QByteArray replyMessage {
108 "HTTP/1.0 200 OK\r\n"
109 "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
110 "Content-Length: " + QByteArray::number(text.size()) + "\r\n\r\n"
111 + text.toUtf8()
112 };
113 socket->write(data: replyMessage);
114 }
115 });
116 QOAuth2AuthorizationCodeFlow oauth2;
117 oauth2.setAccessTokenUrl(webServer.url(path: QLatin1String("accessToken")));
118 ReplyHandler replyHandler;
119 oauth2.setReplyHandler(&replyHandler);
120 oauth2.setRefreshToken(QLatin1String("refresh_token"));
121 QSignalSpy grantedSpy(&oauth2, &QOAuth2AuthorizationCodeFlow::granted);
122 oauth2.refreshAccessToken();
123 QTRY_COMPARE(grantedSpy.count(), 1);
124 QCOMPARE(oauth2.token(), QLatin1String("token"));
125}
126
127void tst_OAuth2::getAndRefreshToken()
128{
129 // In this test we use the grant_type as a token to be able to
130 // identify the token request from the token refresh.
131 WebServer webServer([](const WebServer::HttpRequest &request, QTcpSocket *socket) {
132 if (request.url.path() == QLatin1String("/accessToken")) {
133 const QUrlQuery query(request.body);
134 const QString format = QStringLiteral("access_token=%1&token_type=bearer&expires_in=1&"
135 "refresh_token=refresh_token");
136 const auto text = format.arg(a: query.queryItemValue(key: QLatin1String("grant_type")));
137 const QByteArray replyMessage {
138 "HTTP/1.0 200 OK\r\n"
139 "Content-Type: application/x-www-form-urlencoded; charset=\"utf-8\"\r\n"
140 "Content-Length: " + QByteArray::number(text.size()) + "\r\n\r\n"
141 + text.toUtf8()
142 };
143 socket->write(data: replyMessage);
144 }
145 });
146 QOAuth2AuthorizationCodeFlow oauth2;
147 oauth2.setAuthorizationUrl(webServer.url(path: QLatin1String("authorization")));
148 oauth2.setAccessTokenUrl(webServer.url(path: QLatin1String("accessToken")));
149 ReplyHandler replyHandler;
150 oauth2.setReplyHandler(&replyHandler);
151 connect(sender: &oauth2, signal: &QOAuth2AuthorizationCodeFlow::authorizeWithBrowser, slot: [&](const QUrl &url) {
152 const QUrlQuery query(url.query());
153 replyHandler.emitCallbackReceived(data: QVariantMap {
154 { QLatin1String("code"), QLatin1String("test") },
155 { QLatin1String("state"),
156 query.queryItemValue(key: QLatin1String("state")) }
157 });
158 });
159 QSignalSpy grantedSpy(&oauth2, &QOAuth2AuthorizationCodeFlow::granted);
160 oauth2.grant();
161 QTRY_COMPARE(grantedSpy.count(), 1);
162 QCOMPARE(oauth2.token(), QLatin1String("authorization_code"));
163 grantedSpy.clear();
164 oauth2.refreshAccessToken();
165 QTRY_COMPARE(grantedSpy.count(), 1);
166 QCOMPARE(oauth2.token(), QLatin1String("refresh_token"));
167}
168
169void tst_OAuth2::prepareRequest()
170{
171 QOAuth2AuthorizationCodeFlow oauth2;
172 oauth2.setToken(QStringLiteral("access_token"));
173
174 QNetworkRequest request(QUrl("http://localhost"));
175 oauth2.prepareRequest(request: &request, verb: QByteArray());
176 QCOMPARE(request.rawHeader("Authorization"), QByteArray("Bearer access_token"));
177}
178
179QTEST_MAIN(tst_OAuth2)
180#include "tst_oauth2.moc"
181

source code of qtnetworkauth/tests/auto/oauth2/tst_oauth2.cpp