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#ifndef QT_NO_HTTP
31
32#include <qabstractoauth.h>
33#include <qabstractoauthreplyhandler.h>
34
35#include <private/qabstractoauth_p.h>
36
37#include <QtCore/qurl.h>
38#include <QtCore/qpair.h>
39#include <QtCore/qstring.h>
40#include <QtCore/qdatetime.h>
41#include <QtCore/qurlquery.h>
42#include <QtCore/qjsondocument.h>
43#include <QtCore/qmessageauthenticationcode.h>
44
45#include <QtNetwork/qnetworkrequest.h>
46#include <QtNetwork/qnetworkaccessmanager.h>
47#include <QtNetwork/qnetworkreply.h>
48
49#include <random>
50
51Q_DECLARE_METATYPE(QAbstractOAuth::Error)
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 \class QAbstractOAuth
57 \inmodule QtNetworkAuth
58 \ingroup oauth
59 \brief The QAbstractOAuth class is the base of all
60 implementations of OAuth authentication methods.
61 \since 5.8
62
63 The class defines the basic interface of the OAuth
64 authentication classes. By inheriting this class, you
65 can create custom authentication methods for different web
66 services.
67
68 It also contains some functions to ease the process of
69 implementing different authentication flows.
70*/
71
72/*!
73 \enum QAbstractOAuth::Status
74
75 Indicates the current authentication status.
76
77 \value NotAuthenticated No token has been
78 retrieved.
79
80 \value TemporaryCredentialsReceived Temporary credentials
81 have been received, this status is used in some OAuth
82 authetication methods.
83
84 \value Granted Token credentials have
85 been received and authenticated calls are allowed.
86
87 \value RefreshingToken New token credentials
88 have been requested.
89*/
90
91/*!
92 \enum QAbstractOAuth::Stage
93
94 Identifies an authentication stage. It's passed to a
95 modifyParametersFunction so that it can make different changes to
96 parameters at each call to it during the process of
97 authentication.
98
99 \value RequestingTemporaryCredentials Preparing the temporary
100 credentials request.
101
102 \value RequestingAuthorization Preparing the
103 authorization grant URL.
104
105 \value RequestingAccessToken Preparing the token
106 request.
107
108 \value RefreshingAccessToken Preparing the access
109 token refresh.
110*/
111
112/*!
113 \enum QAbstractOAuth::Error
114
115 Indicates the latest received error.
116
117 \value NoError No error has ocurred.
118
119 \value NetworkError Failed to connect to the server.
120
121 \value ServerError The server answered the
122 request with an error.
123
124 \value OAuthTokenNotFoundError The server's response to
125 a token request provided no token identifier.
126
127 \value OAuthTokenSecretNotFoundError The server's response to
128 a token request provided no token secret.
129
130 \value OAuthCallbackNotVerified The authorization server
131 has not verified the supplied callback URI in the request. This
132 usually happens when the provided callback does not match with
133 the callback supplied during client registration.
134*/
135
136/*!
137 \enum QAbstractOAuth::ContentType
138
139 Indicates the MIME Content-Type of the POST methods in
140 authenticated calls.
141
142 \value WwwFormUrlEncoded Uses
143 application/x-www-form-urlencoded format.
144
145 \value Json Uses
146 application/json format.
147*/
148
149/*!
150 \property QAbstractOAuth::status
151 \brief This property holds the current authentication status.
152*/
153
154/*!
155 \property QAbstractOAuth::extraTokens
156 \brief This property holds the extra tokens received from the
157 server.
158*/
159
160/*!
161 \property QAbstractOAuth::authorizationUrl
162 \brief This property holds the URL used to request the Resource
163 Owner Authorization as described in:
164 \l{https://tools.ietf.org/html/rfc5849#section-2.2}{The OAuth
165 1.0 Protocol: Resource Owner Authorization}
166*/
167
168/*!
169 \property QAbstractOAuth::contentType
170 \brief The Content-Type to use when sending authorization
171 parameters.
172
173 This property controls how parameters are formatted when sent
174 with a POST request. A suitable header is also added.
175*/
176
177/*!
178 \fn void QAbstractOAuth::authorizeWithBrowser(const QUrl &url)
179
180 This signal is emitted when the \a url generated by
181 resourceOwnerAuthorization() is ready to be used in the web
182 browser to allow the application to impersonate the user.
183 \sa resourceOwnerAuthorization()
184*/
185
186/*!
187 \fn void QAbstractOAuth::granted()
188
189 This signal is emitted when the authorization flow finishes
190 successfully.
191*/
192
193/*!
194 \fn QNetworkReply *QAbstractOAuth::head(const QUrl &url, const QVariantMap &parameters)
195
196 Sends an authenticated HEAD request and returns a new
197 QNetworkReply. The \a url and \a parameters are used to create
198 the request.
199
200 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.4}
201 {Hypertext Transfer Protocol -- HTTP/1.1: HEAD}
202*/
203
204/*!
205 \fn QNetworkReply *QAbstractOAuth::get(const QUrl &url, const QVariantMap &parameters)
206
207 Sends an authenticated GET request and returns a new
208 QNetworkReply. The \a url and \a parameters are used to create
209 the request.
210
211 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.3}
212 {Hypertext Transfer Protocol -- HTTP/1.1: GET}
213*/
214
215/*!
216 \fn QNetworkReply *QAbstractOAuth::post(const QUrl &url, const QVariantMap &parameters)
217
218 Sends an authenticated POST request and returns a new
219 QNetworkReply. The \a url and \a parameters are used to create
220 the request.
221
222 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.5}
223 {Hypertext Transfer Protocol -- HTTP/1.1: POST}
224*/
225
226/*!
227 \fn QNetworkReply *QAbstractOAuth::put(const QUrl &url, const QVariantMap &parameters)
228
229 Sends an authenticated PUT request and returns a new
230 QNetworkReply. The \a url and \a parameters are used to create
231 the request.
232
233 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.6}
234 {Hypertext Transfer Protocol -- HTTP/1.1: PUT}
235*/
236
237/*!
238 \fn QNetworkReply *QAbstractOAuth::deleteResource(const QUrl &url, const QVariantMap &parameters)
239
240 Sends an authenticated DELETE request and returns a new
241 QNetworkReply. The \a url and \a parameters are used to create
242 the request.
243
244 \b {See also}: \l {https://tools.ietf.org/html/rfc2616#section-9.7}
245 {Hypertext Transfer Protocol -- HTTP/1.1: DELETE}
246*/
247
248/*!
249 \fn void QAbstractOAuth::grant()
250
251 Override this function to implement the corresponding
252 authentication flow in the subclasses. Client code calls this
253 function to start the authentication workflow. This may require
254 user interaction: for example, asking the user's authorization
255 via a web browser. When the authentication succeeds, it should
256 emit granted(); this gives notice that credentials are ready to
257 be used in authenticated calls.
258*/
259
260QAbstractOAuthPrivate::QAbstractOAuthPrivate(const char *loggingCategory,
261 const QUrl &authorizationUrl,
262 const QString &clientIdentifier,
263 QNetworkAccessManager *manager) :
264 loggingCategory(loggingCategory),
265 clientIdentifier(clientIdentifier),
266 authorizationUrl(authorizationUrl),
267 defaultReplyHandler(new QOAuthOobReplyHandler),
268 networkAccessManagerPointer(manager)
269{}
270
271QAbstractOAuthPrivate::~QAbstractOAuthPrivate()
272{}
273
274QNetworkAccessManager *QAbstractOAuthPrivate::networkAccessManager()
275{
276 Q_Q(QAbstractOAuth);
277 if (!networkAccessManagerPointer)
278 networkAccessManagerPointer = new QNetworkAccessManager(q);
279 return networkAccessManagerPointer.data();
280}
281
282void QAbstractOAuthPrivate::setStatus(QAbstractOAuth::Status newStatus)
283{
284 Q_Q(QAbstractOAuth);
285 if (status != newStatus) {
286 status = newStatus;
287 Q_EMIT q->statusChanged(status);
288 if (status == QAbstractOAuth::Status::Granted)
289 Q_EMIT q->granted();
290 }
291}
292
293QByteArray QAbstractOAuthPrivate::generateRandomString(quint8 length)
294{
295 const char characters[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
296 static std::mt19937 randomEngine(QDateTime::currentDateTime().toMSecsSinceEpoch());
297 std::uniform_int_distribution<int> distribution(0, sizeof(characters) - 2);
298 QByteArray data;
299 data.reserve(asize: length);
300 for (quint8 i = 0; i < length; ++i)
301 data.append(c: characters[distribution(randomEngine)]);
302 return data;
303}
304
305QByteArray QAbstractOAuthPrivate::convertParameters(const QVariantMap &parameters)
306{
307 QByteArray data;
308 switch (contentType) {
309 case QAbstractOAuth::ContentType::Json:
310 data = QJsonDocument::fromVariant(variant: QVariant(parameters)).toJson();
311 break;
312 case QAbstractOAuth::ContentType::WwwFormUrlEncoded: {
313 QUrlQuery query;
314 for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it)
315 query.addQueryItem(key: it.key(), value: it->toString());
316 data = query.toString(encoding: QUrl::FullyEncoded).toUtf8();
317 break;
318 }
319 }
320 return data;
321}
322
323void QAbstractOAuthPrivate::addContentTypeHeaders(QNetworkRequest *request)
324{
325 Q_ASSERT(request);
326
327 switch (contentType) {
328 case QAbstractOAuth::ContentType::WwwFormUrlEncoded:
329 request->setHeader(header: QNetworkRequest::ContentTypeHeader,
330 QStringLiteral("application/x-www-form-urlencoded"));
331 break;
332 case QAbstractOAuth::ContentType::Json:
333 request->setHeader(header: QNetworkRequest::ContentTypeHeader,
334 QStringLiteral("application/json"));
335 break;
336 }
337}
338
339QUrlQuery QAbstractOAuthPrivate::createQuery(const QVariantMap &parameters)
340{
341 QUrlQuery query;
342 for (auto it = parameters.begin(), end = parameters.end(); it != end; ++it)
343 query.addQueryItem(key: it.key(), value: it.value().toString());
344 return query;
345}
346
347QAbstractOAuth::QAbstractOAuth(QAbstractOAuthPrivate &dd, QObject *parent)
348 : QObject(dd, parent)
349{
350 qRegisterMetaType<QAbstractOAuth::Error>();
351}
352
353/*!
354 Destroys the abstract OAuth.
355*/
356QAbstractOAuth::~QAbstractOAuth()
357{}
358
359/*!
360 Returns the current client identifier used in the authentication
361 process.
362
363 \sa setClientIdentifier()
364*/
365QString QAbstractOAuth::clientIdentifier() const
366{
367 Q_D(const QAbstractOAuth);
368 return d->clientIdentifier;
369}
370
371/*!
372 Sets the current client identifier to \a clientIdentifier.
373
374 \sa clientIdentifier()
375*/
376void QAbstractOAuth::setClientIdentifier(const QString &clientIdentifier)
377{
378 Q_D(QAbstractOAuth);
379 if (d->clientIdentifier != clientIdentifier) {
380 d->clientIdentifier = clientIdentifier;
381 Q_EMIT clientIdentifierChanged(clientIdentifier);
382 }
383}
384
385/*!
386 Returns the token used to sign the authenticated requests.
387
388 \sa setToken()
389*/
390QString QAbstractOAuth::token() const
391{
392 Q_D(const QAbstractOAuth);
393 return d->token;
394}
395
396/*!
397 Sets the token used to sign authenticated requests to \a token.
398
399 \sa token()
400*/
401void QAbstractOAuth::setToken(const QString &token)
402{
403 Q_D(QAbstractOAuth);
404 if (d->token != token) {
405 d->token = token;
406 Q_EMIT tokenChanged(token);
407 }
408}
409
410/*!
411 Returns the current network access manager used to send the
412 requests to the server during authentication flows or to make
413 authentication calls.
414
415 \sa setNetworkAccessManager(), QNetworkAccessManager
416*/
417QNetworkAccessManager *QAbstractOAuth::networkAccessManager() const
418{
419 Q_D(const QAbstractOAuth);
420 return d->networkAccessManagerPointer.data();
421}
422
423/*!
424 Sets the network manager to \a networkAccessManager.
425 QAbstractOAuth does not take ownership of
426 \a networkAccessManager. If no custom network access manager is
427 set, an internal network access manager is used.
428 This network access manager will be used
429 to make the request to the authentication server and the
430 authenticated request to the web service.
431
432 \sa networkAccessManager(), QNetworkAccessManager
433*/
434void QAbstractOAuth::setNetworkAccessManager(QNetworkAccessManager *networkAccessManager)
435{
436 Q_D(QAbstractOAuth);
437 if (networkAccessManager != d->networkAccessManagerPointer) {
438 if (d->networkAccessManagerPointer && d->networkAccessManagerPointer->parent() == this)
439 delete d->networkAccessManagerPointer.data();
440 d->networkAccessManagerPointer = networkAccessManager;
441 }
442}
443
444/*!
445 Returns the current authentication status.
446 \sa Status
447*/
448QAbstractOAuth::Status QAbstractOAuth::status() const
449{
450 Q_D(const QAbstractOAuth);
451 return d->status;
452}
453
454/*!
455 Returns the authorization request URL.
456 \sa setAuthorizationUrl()
457*/
458QUrl QAbstractOAuth::authorizationUrl() const
459{
460 Q_D(const QAbstractOAuth);
461 return d->authorizationUrl;
462}
463
464/*!
465 Sets the authorization request URL to \a url. This address
466 will be used to allow the user to grant the application the
467 ability to make authenticated calls on behalf of the user.
468 \sa authorizationUrl()
469*/
470void QAbstractOAuth::setAuthorizationUrl(const QUrl &url)
471{
472 Q_D(QAbstractOAuth);
473 if (d->authorizationUrl != url) {
474 d->authorizationUrl = url;
475 Q_EMIT authorizationUrlChanged(url);
476 }
477}
478
479/*!
480 Sets the current status to \a status. This method is for use
481 by classes based on QAbstractOAuth.
482 \sa status()
483*/
484void QAbstractOAuth::setStatus(QAbstractOAuth::Status status)
485{
486 Q_D(QAbstractOAuth);
487 if (status != d->status) {
488 d->status = status;
489 Q_EMIT statusChanged(status);
490 }
491}
492
493/*!
494 Returns the reply handler currently in use.
495 \sa setReplyHandler(), QAbstractOAuthReplyHandler
496*/
497QAbstractOAuthReplyHandler *QAbstractOAuth::replyHandler() const
498{
499 Q_D(const QAbstractOAuth);
500 return d->replyHandler ? d->replyHandler.data() : d->defaultReplyHandler.data();
501}
502
503/*!
504 Sets the current reply handler to \a handler.
505 \note Does not take ownership of \a handler.
506*/
507void QAbstractOAuth::setReplyHandler(QAbstractOAuthReplyHandler *handler)
508{
509 Q_D(QAbstractOAuth);
510 d->replyHandler = handler;
511}
512
513/*!
514 \since 5.13
515
516 Authorizes the given \a request by adding a header and \a body to
517 it required for authenticated requests.
518
519 The \a verb must be a valid HTTP verb and the same as the one that will be
520 used to send the \a request.
521*/
522void QAbstractOAuth::prepareRequest(QNetworkRequest *request,
523 const QByteArray &verb,
524 const QByteArray &body)
525{
526 Q_D(QAbstractOAuth);
527 d->prepareRequestImpl(request, verb, body);
528}
529
530/*!
531 Returns the current parameter-modification function.
532 \sa setModifyParametersFunction(), Stage
533*/
534QAbstractOAuth::ModifyParametersFunction QAbstractOAuth::modifyParametersFunction() const
535{
536 Q_D(const QAbstractOAuth);
537 return d->modifyParametersFunction;
538}
539
540/*!
541 Sets the parameter-modification function \a modifyParametersFunction.
542 This function is used to customize the parameters sent to the server
543 during a specified authorization stage. The number of calls to this
544 function depends on the flow used during the authentication.
545 \sa modifyParametersFunction(), Stage
546*/
547void QAbstractOAuth::setModifyParametersFunction(
548 const QAbstractOAuth::ModifyParametersFunction &modifyParametersFunction)
549{
550 Q_D(QAbstractOAuth);
551 d->modifyParametersFunction = modifyParametersFunction;
552}
553
554/*!
555 Returns the current Content-Type used in authenticated calls.
556 \sa setContentType(), post()
557*/
558QAbstractOAuth::ContentType QAbstractOAuth::contentType() const
559{
560 Q_D(const QAbstractOAuth);
561 return d->contentType;
562}
563
564/*!
565 Sets the current Content-Type to \a contentType.
566*/
567void QAbstractOAuth::setContentType(QAbstractOAuth::ContentType contentType)
568{
569 Q_D(QAbstractOAuth);
570 if (d->contentType != contentType) {
571 d->contentType = contentType;
572 Q_EMIT contentTypeChanged(contentType);
573 }
574}
575
576/*!
577 Returns the extra tokens received from the server during
578 authentication.
579 \sa extraTokensChanged()
580*/
581QVariantMap QAbstractOAuth::extraTokens() const
582{
583 Q_D(const QAbstractOAuth);
584 return d->extraTokens;
585}
586
587/*!
588 Returns the current callback string corresponding to the
589 current reply handler. The returned string is the string
590 sent to the server to specify the callback URI, or the word
591 identifying the alternative method in headless devices.
592 \sa replyHandler(), setReplyHandler()
593*/
594QString QAbstractOAuth::callback() const
595{
596 Q_D(const QAbstractOAuth);
597 return d->replyHandler ? d->replyHandler->callback()
598 : d->defaultReplyHandler->callback();
599}
600
601/*!
602 Builds the resource owner authorization URL to be used in the web
603 browser: \a url is used as the base URL and the query is created
604 using \a parameters. When the URL is ready, the
605 authorizeWithBrowser() signal will be emitted with the generated
606 URL.
607 \sa authorizeWithBrowser()
608*/
609void QAbstractOAuth::resourceOwnerAuthorization(const QUrl &url, const QVariantMap &parameters)
610{
611 QUrl u = url;
612 u.setQuery(QAbstractOAuthPrivate::createQuery(parameters));
613 Q_EMIT authorizeWithBrowser(url: u);
614}
615
616/*!
617 Generates a random string which could be used as state or nonce.
618 The parameter \a length determines the size of the generated
619 string.
620
621 \b {See also}: \l {https://tools.ietf.org/html/rfc5849#section-3.3}{The
622 OAuth 1.0 Protocol: Nonce and Timestamp}.
623*/
624QByteArray QAbstractOAuth::generateRandomString(quint8 length)
625{
626 return QAbstractOAuthPrivate::generateRandomString(length);
627}
628
629QT_END_NAMESPACE
630
631#endif // QT_NO_HTTP
632

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