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 | |
25 | QT_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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 | |
243 | QAbstractOAuthPrivate::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 | |
254 | QAbstractOAuthPrivate::~QAbstractOAuthPrivate() |
255 | {} |
256 | |
257 | QNetworkAccessManager *QAbstractOAuthPrivate::networkAccessManager() |
258 | { |
259 | Q_Q(QAbstractOAuth); |
260 | if (!networkAccessManagerPointer) |
261 | networkAccessManagerPointer = new QNetworkAccessManager(q); |
262 | return networkAccessManagerPointer.data(); |
263 | } |
264 | |
265 | void 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 | |
276 | QByteArray 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 | |
288 | QByteArray QAbstractOAuthPrivate::convertParameters(const QVariantMap ¶meters) |
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 | |
306 | void QAbstractOAuthPrivate::(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 | |
322 | QUrlQuery QAbstractOAuthPrivate::createQuery(const QMultiMap<QString, QVariant> ¶meters) |
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 | |
330 | QAbstractOAuth::QAbstractOAuth(QAbstractOAuthPrivate &dd, QObject *parent) |
331 | : QObject(dd, parent) |
332 | { |
333 | qRegisterMetaType<QAbstractOAuth::Error>(); |
334 | } |
335 | |
336 | /*! |
337 | Destroys the abstract OAuth. |
338 | */ |
339 | QAbstractOAuth::~QAbstractOAuth() |
340 | {} |
341 | |
342 | /*! |
343 | Returns the current client identifier used in the authentication |
344 | process. |
345 | |
346 | \sa setClientIdentifier() |
347 | */ |
348 | QString 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 | */ |
359 | void 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 | */ |
373 | QString 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 | */ |
384 | void 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 | */ |
400 | QNetworkAccessManager *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 | */ |
417 | void 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 | */ |
431 | QAbstractOAuth::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 | */ |
441 | QUrl 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 | */ |
453 | void 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 | */ |
467 | void 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 | */ |
480 | QAbstractOAuthReplyHandler *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 | */ |
490 | void 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 | */ |
511 | QAbstractOAuth::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 | */ |
524 | void 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 | */ |
535 | QAbstractOAuth::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 | */ |
544 | void 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 | */ |
558 | QVariantMap QAbstractOAuth::() 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 | */ |
571 | QString 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 | */ |
586 | void QAbstractOAuth::resourceOwnerAuthorization(const QUrl &url, const QMultiMap<QString, QVariant> ¶meters) |
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 | */ |
601 | QByteArray QAbstractOAuth::generateRandomString(quint8 length) |
602 | { |
603 | return QAbstractOAuthPrivate::generateRandomString(length); |
604 | } |
605 | |
606 | QT_END_NAMESPACE |
607 | |
608 | #endif // QT_NO_HTTP |
609 | |