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 Qt Network Auth module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
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 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtNetwork/qtnetwork-config.h>
31
32#ifndef QT_NO_HTTP
33
34#include <qabstractoauth2.h>
35#include <private/qabstractoauth2_p.h>
36
37#include <QtCore/qurl.h>
38#include <QtCore/qurlquery.h>
39#include <QtCore/qbytearray.h>
40#include <QtCore/qmessageauthenticationcode.h>
41
42#include <QtNetwork/qnetworkreply.h>
43#include <QtNetwork/qnetworkrequest.h>
44#include <QtNetwork/qnetworkaccessmanager.h>
45#include <QtNetwork/qhttpmultipart.h>
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 \class QAbstractOAuth2
51 \inmodule QtNetworkAuth
52 \ingroup oauth
53 \brief The QAbstractOAuth2 class is the base of all
54 implementations of OAuth 2 authentication methods.
55 \since 5.8
56
57 The class defines the basic interface of the OAuth 2
58 authentication classes. By inheriting this class, you
59 can create custom authentication methods using the OAuth 2
60 standard for different web services.
61
62 A description of how OAuth 2 works can be found in:
63 \l {https://tools.ietf.org/html/rfc6749}{The OAuth 2.0
64 Authorization Framework}
65*/
66/*!
67 \property QAbstractOAuth2::scope
68 \brief This property holds the desired scope which defines the
69 permissions requested by the client.
70*/
71
72/*!
73 \property QAbstractOAuth2::userAgent
74 This property holds the User-Agent header used to create the
75 network requests.
76
77 The default value is "QtOAuth/1.0 (+https://www.qt.io)".
78*/
79
80/*!
81 \property QAbstractOAuth2::clientIdentifierSharedKey
82 This property holds the client shared key used as a password if
83 the server requires authentication to request the token.
84*/
85
86/*!
87 \property QAbstractOAuth2::state
88 This property holds the string sent to the server during
89 authentication. The state is used to identify and validate the
90 request when the callback is received.
91*/
92
93/*!
94 \property QAbstractOAuth2::expiration
95 This property holds the expiration time of the current access
96 token.
97*/
98
99/*!
100 \fn QAbstractOAuth2::error(const QString &error, const QString &errorDescription, const QUrl &uri)
101
102 Signal emitted when the server responds to the request with an
103 error: \a error is the name of the error; \a errorDescription describes
104 the error and \a uri is an optional URI containing more
105 information about the error.
106*/
107
108/*!
109 \fn QAbstractOAuth2::authorizationCallbackReceived(const QVariantMap &data)
110
111 Signal emitted when the reply server receives the authorization
112 callback from the server: \a data contains the values received
113 from the server.
114*/
115
116using Key = QAbstractOAuth2Private::OAuth2KeyString;
117const QString Key::accessToken = QStringLiteral("access_token");
118const QString Key::apiKey = QStringLiteral("api_key");
119const QString Key::clientIdentifier = QStringLiteral("client_id");
120const QString Key::clientSharedSecret = QStringLiteral("client_secret");
121const QString Key::code = QStringLiteral("code");
122const QString Key::error = QStringLiteral("error");
123const QString Key::errorDescription = QStringLiteral("error_description");
124const QString Key::errorUri = QStringLiteral("error_uri");
125const QString Key::expiresIn = QStringLiteral("expires_in");
126const QString Key::grantType = QStringLiteral("grant_type");
127const QString Key::redirectUri = QStringLiteral("redirect_uri");
128const QString Key::refreshToken = QStringLiteral("refresh_token");
129const QString Key::responseType = QStringLiteral("response_type");
130const QString Key::scope = QStringLiteral("scope");
131const QString Key::state = QStringLiteral("state");
132const QString Key::tokenType = QStringLiteral("token_type");
133
134QAbstractOAuth2Private::QAbstractOAuth2Private(const QPair<QString, QString> &clientCredentials,
135 const QUrl &authorizationUrl,
136 QNetworkAccessManager *manager) :
137 QAbstractOAuthPrivate("qt.networkauth.oauth2",
138 authorizationUrl,
139 clientCredentials.first,
140 manager),
141 clientIdentifierSharedKey(clientCredentials.second)
142{}
143
144QAbstractOAuth2Private::~QAbstractOAuth2Private()
145{}
146
147QString QAbstractOAuth2Private::generateRandomState()
148{
149 return QString::fromUtf8(str: QAbstractOAuthPrivate::generateRandomString(length: 8));
150}
151
152QNetworkRequest QAbstractOAuth2Private::createRequest(QUrl url, const QVariantMap *parameters)
153{
154 QUrlQuery query(url.query());
155
156 QNetworkRequest request;
157 if (parameters) {
158 for (auto it = parameters->begin(), end = parameters->end(); it != end; ++it)
159 query.addQueryItem(key: it.key(), value: it.value().toString());
160 url.setQuery(query);
161 } else { // POST, PUT request
162 addContentTypeHeaders(request: &request);
163 }
164
165 request.setUrl(url);
166 request.setHeader(header: QNetworkRequest::UserAgentHeader, value: userAgent);
167 const QString bearer = bearerFormat.arg(a: token);
168 request.setRawHeader(headerName: "Authorization", value: bearer.toUtf8());
169 return request;
170}
171
172void QAbstractOAuth2Private::prepareRequestImpl(QNetworkRequest *request,
173 const QByteArray &verb,
174 const QByteArray &body)
175{
176 Q_UNUSED(verb)
177 Q_UNUSED(body)
178 request->setHeader(header: QNetworkRequest::UserAgentHeader, value: userAgent);
179 const QString bearer = bearerFormat.arg(a: token);
180 request->setRawHeader(headerName: "Authorization", value: bearer.toUtf8());
181}
182
183/*!
184 Constructs a QAbstractOAuth2 object using \a parent as parent.
185*/
186QAbstractOAuth2::QAbstractOAuth2(QObject *parent) :
187 QAbstractOAuth2(nullptr, parent)
188{}
189
190/*!
191 Constructs a QAbstractOAuth2 object using \a parent as parent and
192 sets \a manager as the network access manager.
193*/
194QAbstractOAuth2::QAbstractOAuth2(QNetworkAccessManager *manager, QObject *parent) :
195 QAbstractOAuth(*new QAbstractOAuth2Private(qMakePair(x: QString(), y: QString()),
196 QUrl(),
197 manager),
198 parent)
199{}
200
201QAbstractOAuth2::QAbstractOAuth2(QAbstractOAuth2Private &dd, QObject *parent) :
202 QAbstractOAuth(dd, parent)
203{}
204
205void QAbstractOAuth2::setResponseType(const QString &responseType)
206{
207 Q_D(QAbstractOAuth2);
208 if (d->responseType != responseType) {
209 d->responseType = responseType;
210 Q_EMIT responseTypeChanged(responseType);
211 }
212}
213
214/*!
215 Destroys the QAbstractOAuth2 instance.
216*/
217QAbstractOAuth2::~QAbstractOAuth2()
218{}
219
220/*!
221 The returned URL is based on \a url, combining it with the given
222 \a parameters and the access token.
223*/
224QUrl QAbstractOAuth2::createAuthenticatedUrl(const QUrl &url, const QVariantMap &parameters)
225{
226 Q_D(const QAbstractOAuth2);
227 if (Q_UNLIKELY(d->token.isEmpty())) {
228 qCWarning(d->loggingCategory, "Empty access token");
229 return QUrl();
230 }
231 QUrl ret = url;
232 QUrlQuery query(ret.query());
233 query.addQueryItem(key: Key::accessToken, value: d->token);
234 for (auto it = parameters.begin(), end = parameters.end(); it != end ;++it)
235 query.addQueryItem(key: it.key(), value: it.value().toString());
236 ret.setQuery(query);
237 return ret;
238}
239
240/*!
241 Sends an authenticated HEAD request and returns a new
242 QNetworkReply. The \a url and \a parameters are used to create
243 the request.
244
245 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.4}
246 {Hypertext Transfer Protocol -- HTTP/1.1: HEAD}
247*/
248QNetworkReply *QAbstractOAuth2::head(const QUrl &url, const QVariantMap &parameters)
249{
250 Q_D(QAbstractOAuth2);
251 QNetworkReply *reply = d->networkAccessManager()->head(request: d->createRequest(url, parameters: &parameters));
252 connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); });
253 return reply;
254}
255
256/*!
257 Sends an authenticated GET request and returns a new
258 QNetworkReply. The \a url and \a parameters are used to create
259 the request.
260
261 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.3}
262 {Hypertext Transfer Protocol -- HTTP/1.1: GET}
263*/
264QNetworkReply *QAbstractOAuth2::get(const QUrl &url, const QVariantMap &parameters)
265{
266 Q_D(QAbstractOAuth2);
267 QNetworkReply *reply = d->networkAccessManager()->get(request: d->createRequest(url, parameters: &parameters));
268 connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); });
269 return reply;
270}
271
272/*!
273 Sends an authenticated POST request and returns a new
274 QNetworkReply. The \a url and \a parameters are used to create
275 the request.
276
277 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.5}
278 {Hypertext Transfer Protocol -- HTTP/1.1: POST}
279*/
280QNetworkReply *QAbstractOAuth2::post(const QUrl &url, const QVariantMap &parameters)
281{
282 Q_D(QAbstractOAuth2);
283 const auto data = d->convertParameters(parameters);
284 return post(url, data);
285}
286
287/*!
288 \since 5.10
289
290 \overload
291
292 Sends an authenticated POST request and returns a new
293 QNetworkReply. The \a url and \a data are used to create
294 the request.
295
296 \sa post(), {https://tools.ietf.org/html/rfc2616#section-9.6}
297 {Hypertext Transfer Protocol -- HTTP/1.1: POST}
298*/
299QNetworkReply *QAbstractOAuth2::post(const QUrl &url, const QByteArray &data)
300{
301 Q_D(QAbstractOAuth2);
302 QNetworkReply *reply = d->networkAccessManager()->post(request: d->createRequest(url), data);
303 connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); });
304 return reply;
305}
306
307/*!
308 \since 5.10
309
310 \overload
311
312 Sends an authenticated POST request and returns a new
313 QNetworkReply. The \a url and \a multiPart are used to create
314 the request.
315
316 \sa post(), QHttpMultiPart, {https://tools.ietf.org/html/rfc2616#section-9.6}
317 {Hypertext Transfer Protocol -- HTTP/1.1: POST}
318*/
319QNetworkReply *QAbstractOAuth2::post(const QUrl &url, QHttpMultiPart *multiPart)
320{
321 Q_D(QAbstractOAuth2);
322 QNetworkReply *reply = d->networkAccessManager()->post(request: d->createRequest(url), multiPart);
323 connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); });
324 return reply;
325}
326
327/*!
328 Sends an authenticated PUT request and returns a new
329 QNetworkReply. The \a url and \a parameters are used to create
330 the request.
331
332 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.6}
333 {Hypertext Transfer Protocol -- HTTP/1.1: PUT}
334*/
335QNetworkReply *QAbstractOAuth2::put(const QUrl &url, const QVariantMap &parameters)
336{
337 Q_D(QAbstractOAuth2);
338 const auto data = d->convertParameters(parameters);
339 return put(url, data);
340}
341
342/*!
343 \since 5.10
344
345 \overload
346
347 Sends an authenticated PUT request and returns a new
348 QNetworkReply. The \a url and \a data are used to create
349 the request.
350
351 \sa put(), {https://tools.ietf.org/html/rfc2616#section-9.6}
352 {Hypertext Transfer Protocol -- HTTP/1.1: PUT}
353*/
354QNetworkReply *QAbstractOAuth2::put(const QUrl &url, const QByteArray &data)
355{
356 Q_D(QAbstractOAuth2);
357 QNetworkReply *reply = d->networkAccessManager()->put(request: d->createRequest(url), data);
358 connect(sender: reply, signal: &QNetworkReply::finished, slot: std::bind(f: &QAbstractOAuth::finished, args: this, args&: reply));
359 return reply;
360}
361
362/*!
363 \since 5.10
364
365 \overload
366
367 Sends an authenticated PUT request and returns a new
368 QNetworkReply. The \a url and \a multiPart are used to create
369 the request.
370
371 \sa put(), QHttpMultiPart, {https://tools.ietf.org/html/rfc2616#section-9.6}
372 {Hypertext Transfer Protocol -- HTTP/1.1: PUT}
373*/
374QNetworkReply *QAbstractOAuth2::put(const QUrl &url, QHttpMultiPart *multiPart)
375{
376 Q_D(QAbstractOAuth2);
377 QNetworkReply *reply = d->networkAccessManager()->put(request: d->createRequest(url), multiPart);
378 connect(sender: reply, signal: &QNetworkReply::finished, slot: std::bind(f: &QAbstractOAuth::finished, args: this, args&: reply));
379 return reply;
380}
381
382/*!
383 Sends an authenticated DELETE request and returns a new
384 QNetworkReply. The \a url and \a parameters are used to create
385 the request.
386
387 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.7}
388 {Hypertext Transfer Protocol -- HTTP/1.1: DELETE}
389*/
390QNetworkReply *QAbstractOAuth2::deleteResource(const QUrl &url, const QVariantMap &parameters)
391{
392 Q_D(QAbstractOAuth2);
393 QNetworkReply *reply = d->networkAccessManager()->deleteResource(
394 request: d->createRequest(url, parameters: &parameters));
395 connect(sender: reply, signal: &QNetworkReply::finished, slot: [this, reply]() { emit finished(reply); });
396 return reply;
397}
398
399QString QAbstractOAuth2::scope() const
400{
401 Q_D(const QAbstractOAuth2);
402 return d->scope;
403}
404
405void QAbstractOAuth2::setScope(const QString &scope)
406{
407 Q_D(QAbstractOAuth2);
408 if (d->scope != scope) {
409 d->scope = scope;
410 Q_EMIT scopeChanged(scope);
411 }
412}
413
414QString QAbstractOAuth2::userAgent() const
415{
416 Q_D(const QAbstractOAuth2);
417 return d->userAgent;
418}
419
420void QAbstractOAuth2::setUserAgent(const QString &userAgent)
421{
422 Q_D(QAbstractOAuth2);
423 if (d->userAgent != userAgent) {
424 d->userAgent = userAgent;
425 Q_EMIT userAgentChanged(userAgent);
426 }
427}
428
429/*!
430 Returns the \l {https://tools.ietf.org/html/rfc6749#section-3.1.1}
431 {response_type} used.
432*/
433QString QAbstractOAuth2::responseType() const
434{
435 Q_D(const QAbstractOAuth2);
436 return d->responseType;
437}
438
439QString QAbstractOAuth2::clientIdentifierSharedKey() const
440{
441 Q_D(const QAbstractOAuth2);
442 return d->clientIdentifierSharedKey;
443}
444
445void QAbstractOAuth2::setClientIdentifierSharedKey(const QString &clientIdentifierSharedKey)
446{
447 Q_D(QAbstractOAuth2);
448 if (d->clientIdentifierSharedKey != clientIdentifierSharedKey) {
449 d->clientIdentifierSharedKey = clientIdentifierSharedKey;
450 Q_EMIT clientIdentifierSharedKeyChanged(clientIdentifierSharedKey);
451 }
452}
453
454QString QAbstractOAuth2::state() const
455{
456 Q_D(const QAbstractOAuth2);
457 return d->state;
458}
459
460void QAbstractOAuth2::setState(const QString &state)
461{
462 Q_D(QAbstractOAuth2);
463 if (state != d->state) {
464 d->state = state;
465 Q_EMIT stateChanged(state);
466 }
467}
468
469QDateTime QAbstractOAuth2::expirationAt() const
470{
471 Q_D(const QAbstractOAuth2);
472 return d->expiresAt;
473}
474
475/*!
476 \brief Gets the current refresh token.
477
478 Refresh tokens usually have longer lifespans than access tokens,
479 so it makes sense to save them for later use.
480
481 Returns the current refresh token or an empty string, if
482 there is no refresh token available.
483*/
484QString QAbstractOAuth2::refreshToken() const
485{
486 Q_D(const QAbstractOAuth2);
487 return d->refreshToken;
488}
489
490/*!
491 \brief Sets the new refresh token \a refreshToken to be used.
492
493 A custom refresh token can be used to refresh the access token via this method and then
494 the access token can be refreshed via QOAuth2AuthorizationCodeFlow::refreshAccessToken().
495
496*/
497void QAbstractOAuth2::setRefreshToken(const QString &refreshToken)
498{
499 Q_D(QAbstractOAuth2);
500 if (d->refreshToken != refreshToken) {
501 d->refreshToken = refreshToken;
502 Q_EMIT refreshTokenChanged(refreshToken);
503 }
504}
505
506QT_END_NAMESPACE
507
508#endif // QT_NO_HTTP
509

source code of qtnetworkauth/src/oauth/qabstractoauth2.cpp