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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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