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

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