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 | |
24 | QT_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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 ¶meters) |
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 | |
242 | QAbstractOAuthPrivate::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 | |
253 | QAbstractOAuthPrivate::~QAbstractOAuthPrivate() |
254 | {} |
255 | |
256 | QNetworkAccessManager *QAbstractOAuthPrivate::networkAccessManager() |
257 | { |
258 | Q_Q(QAbstractOAuth); |
259 | if (!networkAccessManagerPointer) |
260 | networkAccessManagerPointer = new QNetworkAccessManager(q); |
261 | return networkAccessManagerPointer.data(); |
262 | } |
263 | |
264 | void 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 | |
275 | QByteArray 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 | |
298 | void 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) |
308 | QByteArray QAbstractOAuthPrivate::convertParameters(const QVariantMap ¶meters) |
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 | |
326 | void 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 | |
342 | QUrlQuery QAbstractOAuthPrivate::createQuery(const QMultiMap<QString, QVariant> ¶meters) |
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 | |
350 | QAbstractOAuth::QAbstractOAuth(QAbstractOAuthPrivate &dd, QObject *parent) |
351 | : QObject(dd, parent) |
352 | { |
353 | qRegisterMetaType<QAbstractOAuth::Error>(); |
354 | } |
355 | |
356 | /*! |
357 | Destroys the abstract OAuth. |
358 | */ |
359 | QAbstractOAuth::~QAbstractOAuth() |
360 | {} |
361 | |
362 | /*! |
363 | Returns the current client identifier used in the authentication |
364 | process. |
365 | |
366 | \sa setClientIdentifier() |
367 | */ |
368 | QString 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 | */ |
379 | void 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 | */ |
393 | QString 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 | */ |
404 | void 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 | */ |
420 | QNetworkAccessManager *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 | */ |
437 | void 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 | */ |
451 | QAbstractOAuth::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 | */ |
461 | QUrl 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 | */ |
473 | void 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 | */ |
487 | void 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 | */ |
500 | QAbstractOAuthReplyHandler *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 | */ |
510 | void 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 | */ |
531 | QAbstractOAuth::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 | */ |
544 | void 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 | */ |
555 | QAbstractOAuth::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 | */ |
564 | void 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 | */ |
578 | QVariantMap 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 | */ |
591 | QString 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 | */ |
606 | void QAbstractOAuth::resourceOwnerAuthorization(const QUrl &url, const QMultiMap<QString, QVariant> ¶meters) |
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 | */ |
622 | QByteArray QAbstractOAuth::generateRandomString(quint8 length) |
623 | { |
624 | return QAbstractOAuthPrivate::generateRandomBase64String(length); |
625 | } |
626 | |
627 | QT_END_NAMESPACE |
628 |
Definitions
- QAbstractOAuthPrivate
- ~QAbstractOAuthPrivate
- networkAccessManager
- setStatus
- generateRandomBase64String
- setExtraTokens
- convertParameters
- addContentTypeHeaders
- createQuery
- QAbstractOAuth
- ~QAbstractOAuth
- clientIdentifier
- setClientIdentifier
- token
- setToken
- networkAccessManager
- setNetworkAccessManager
- status
- authorizationUrl
- setAuthorizationUrl
- setStatus
- replyHandler
- setReplyHandler
- modifyParametersFunction
- setModifyParametersFunction
- contentType
- setContentType
- extraTokens
- callback
- resourceOwnerAuthorization
Learn to use CMake with our Intro Training
Find out more