Commit 54a8c221 authored by Jesus Fernandez's avatar Jesus Fernandez

Google Calendar Example

parent ae8f4c7a
......@@ -81,10 +81,12 @@ public:
QAbstractOAuth2::Credential token;
QString scope;
QString userAgent = "qoauth/0.1";
const QString bearerFormat = QStringLiteral("Bearer %1");
struct OAuth2KeyString
{
static const QString accessToken;
static const QString apiKey;
static const QString clientId;
static const QString code;
static const QString error;
......
......@@ -70,13 +70,14 @@ public:
QOAuth2AuthorizationCodeFlowPrivate(QNetworkAccessManager *manager, quint16 port);
void handleCallback(const QVariantMap &data, const QString state);
void accessTokenRequestFinished();
void accessTokenRequestFinished(QNetworkReply *reply);
void authenticate(QNetworkReply *reply, QAuthenticator *authenticator);
private:
QUrl accessTokenUrl;
QString tokenType;
QDateTime expiresAt;
QString refreshToken;
QPointer<QNetworkReply> accessTokenReply;
};
QT_END_NAMESPACE
......
......@@ -75,6 +75,8 @@ void QAbstractOAuthPrivate::setStatus(QAbstractOAuth::Status newStatus)
if (status != newStatus) {
status = newStatus;
Q_EMIT q->statusChanged(status);
if (status == QAbstractOAuth::Status::Granted)
Q_EMIT q->granted();
}
}
......
......@@ -56,6 +56,7 @@ QT_BEGIN_NAMESPACE
typedef QAbstractOAuth2Private::OAuth2KeyString Key;
const QString Key::accessToken = QStringLiteral("access_token");
const QString Key::apiKey = QStringLiteral("api_key");
const QString Key::clientId = QStringLiteral("client_id");
const QString Key::code = QStringLiteral("code");
const QString Key::error = QStringLiteral("error");
......@@ -120,10 +121,9 @@ QNetworkReply *QAbstractOAuth2::get(const QNetworkRequest &req, const QVariantMa
QNetworkRequest request(req);
request.setUrl(url);
QByteArray bearer("bearer ");
bearer.append(d->token.key.toLocal8Bit());
request.setHeader(QNetworkRequest::UserAgentHeader, d->userAgent);
request.setRawHeader("Authorization", bearer);
const QString bearer = d->bearerFormat.arg(d->token.key);
request.setRawHeader("Authorization", bearer.toUtf8());
return d->networkAccessManager->get(request);
}
......@@ -142,10 +142,9 @@ QNetworkReply *QAbstractOAuth2::post(const QNetworkRequest &req, const QVariantM
QNetworkRequest request(req);
request.setUrl(url);
QByteArray bearer("bearer ");
bearer.append(d->token.key.toLocal8Bit());
request.setHeader(QNetworkRequest::UserAgentHeader, d->userAgent);
request.setRawHeader("Authorization", bearer);
const QString bearer = d->bearerFormat.arg(d->token.key);
request.setRawHeader("Authorization", bearer.toUtf8());
return d->networkAccessManager->post(request, QByteArray());
}
......
......@@ -48,6 +48,7 @@
#include <qurlquery.h>
#include <qjsonobject.h>
#include <qjsondocument.h>
#include <qauthenticator.h>
#include <qoauthhttpserverreplyhandler.h>
#include <functional>
......@@ -102,14 +103,15 @@ void QOAuth2AuthorizationCodeFlowPrivate::handleCallback(const QVariantMap &data
q->requestAccessToken(code);
}
void QOAuth2AuthorizationCodeFlowPrivate::accessTokenRequestFinished()
void QOAuth2AuthorizationCodeFlowPrivate::accessTokenRequestFinished(QNetworkReply *reply)
{
Q_Q(QOAuth2AuthorizationCodeFlow);
typedef QAbstractOAuth2Private::OAuth2KeyString Key;
auto reply = qobject_cast<QNetworkReply*>(senders->sender);
Q_ASSERT(reply);
if (reply != accessTokenReply)
return;
if (reply->error() == QNetworkReply::UnknownNetworkError) {
return;
qWarning("QOAuth2AuthorizationCodeFlow: %s", qPrintable(reply->errorString()));
setStatus(QAbstractOAuth::Status::NotAuthenticated);
return;
......@@ -120,6 +122,12 @@ void QOAuth2AuthorizationCodeFlowPrivate::accessTokenRequestFinished()
Q_ASSERT(document.isObject());
const auto object = document.object();
if (object.contains(Key::error)) {
const QString error = object.value(Key::error).toString();
qCritical("QOAuth2AuthorizationCodeFlow Error: %s", qPrintable(error));
return;
}
const auto accessToken = object.value(Key::accessToken).toString();
tokenType = object.value(Key::tokenType).toString();
const auto expiresIn = object.value(Key::expiresIn).toInt(-1);
......@@ -135,6 +143,18 @@ void QOAuth2AuthorizationCodeFlowPrivate::accessTokenRequestFinished()
setStatus(QAbstractOAuth::Status::Granted);
}
void QOAuth2AuthorizationCodeFlowPrivate::authenticate(QNetworkReply *reply,
QAuthenticator *authenticator)
{
if (reply == accessTokenReply){
const auto url = reply->url();
if (url == accessTokenUrl) {
authenticator->setUser(consumer.key);
authenticator->setPassword("");
}
}
}
QOAuth2AuthorizationCodeFlow::QOAuth2AuthorizationCodeFlow(QNetworkAccessManager *manager,
quint16 port,
QObject *parent)
......@@ -212,6 +232,14 @@ void QOAuth2AuthorizationCodeFlow::setAccessTokenUrl(const QUrl &accessTokenUrl)
void QOAuth2AuthorizationCodeFlow::grant()
{
Q_D(const QOAuth2AuthorizationCodeFlow);
if (d->authorizationUrl.isEmpty()) {
qCritical("QOAuth2AuthorizationCodeFlow: No authenticate Url set");
return;
} else if (d->accessTokenUrl.isEmpty()) {
qCritical("QOAuth2AuthorizationCodeFlow: No request access token Url set");
return;
}
const QUrl url = buildAuthenticateUrl();
Q_EMIT authorizationRequested(url);
}
......@@ -230,6 +258,7 @@ QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &param
p.insert(Key::redirectUri, callback());
p.insert(Key::scope, d->scope);
p.insert(Key::state, state);
p.insert(Key::apiKey, QVariant());
if (d->modifyParametersFunction)
d->modifyParametersFunction(Stage::RequestingAuthorization, &p);
url.setQuery(d->createQuery(p));
......@@ -237,6 +266,7 @@ QUrl QOAuth2AuthorizationCodeFlow::buildAuthenticateUrl(const QVariantMap &param
std::placeholders::_1, state);
connect(d->replyHandler.data(), &QAbstractOAuthReplyHandler::callbackReceived, bind);
setStatus(QAbstractOAuth::Status::NotAuthenticated);
qDebug() << url;
return url;
}
......@@ -247,22 +277,24 @@ void QOAuth2AuthorizationCodeFlow::requestAccessToken(const QString &code)
QVariantMap parameters;
QNetworkRequest request(d->accessTokenUrl);
QString authentication = QString::fromUtf8("%1:").arg(d->consumer.key);
QUrlQuery query;
parameters.insert(Key::grantType, grantType());
parameters.insert(Key::code, code);
parameters.insert(Key::redirectUri, callback());
parameters.insert(Key::clientId, d->consumer.key);
parameters.insert(Key::code, QUrl::toPercentEncoding(code));
parameters.insert(Key::redirectUri, QUrl::toPercentEncoding(callback()));
parameters.insert(Key::clientId, QUrl::toPercentEncoding(d->consumer.key));
if (d->modifyParametersFunction)
d->modifyParametersFunction(Stage::RequestingAccessToken, &parameters);
query = QAbstractOAuthPrivate::createQuery(parameters);
request.setRawHeader("Authorization", "Basic " + authentication.toLocal8Bit().toBase64());
request.setHeader(QNetworkRequest::ContentTypeHeader,
QStringLiteral("application/x-www-form-urlencoded"));
auto reply = d->networkAccessManager->post(request, query.toString().toUtf8());
QObjectPrivate::connect(reply, &QNetworkReply::finished,
const auto data = query.toString(QUrl::FullyEncoded);
d->accessTokenReply = d->networkAccessManager->post(request, data.toUtf8());
QObjectPrivate::connect(d->networkAccessManager, &QNetworkAccessManager::finished,
d, &QOAuth2AuthorizationCodeFlowPrivate::accessTokenRequestFinished);
QObjectPrivate::connect(d->networkAccessManager, &QNetworkAccessManager::authenticationRequired,
d, &QOAuth2AuthorizationCodeFlowPrivate::authenticate,
Qt::UniqueConnection);
}
QT_END_NAMESPACE
......
......@@ -125,7 +125,7 @@ QString QOAuthHttpServerReplyHandler::callback() const
Q_D(const QOAuthHttpServerReplyHandler);
Q_ASSERT(d->httpServer.isListening());
const QUrl url(QString::fromLatin1("http://localhost:%1").arg(d->httpServer.serverPort()));
const QUrl url(QString::fromLatin1("http://localhost:%1/").arg(d->httpServer.serverPort()));
return url.toString(QUrl::EncodeDelimiters);
}
......
......@@ -2,4 +2,5 @@ TEMPLATE = subdirs
SUBDIRS += \
twittertimeline \
redditclient
redditclient \
qtgcal
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <qoauth2authorizationcodeflow.h>
#include <QtCore>
#include <QtNetwork>
#include <QtWidgets>
const QString webString = QStringLiteral("web");
const QString clientIdString = QStringLiteral("client_id");
const QString authUriString = QStringLiteral("auth_uri");
const QString tokenUriString = QStringLiteral("token_uri");
const QString clientSecretString = QStringLiteral("client_secret");
const QString redirectUriString = QStringLiteral("redirect_uris");
QMap<QString, QString> readClientJson(const QString &path)
{
QMap<QString, QString> ret;
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCritical() << "Failed to load file";
return ret;
}
auto document = QJsonDocument::fromJson(file.readAll());
Q_ASSERT(document.isObject());
auto webObject = document.object()[webString].toObject();
const auto saveValue = [&](const QString &key) {
ret.insert(key, webObject[key].toString());
};
saveValue(clientIdString);
saveValue(authUriString);
saveValue(authUriString);
saveValue(tokenUriString);
saveValue(clientSecretString);
ret.insert(redirectUriString, webObject[redirectUriString].toArray().first().toString());
return ret;
}
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QMap<QString, QString> clientInfo;
QCommandLineParser parser;
parser.addPositionalArgument("client_json", "Specifies the file downloaded from Google APIs "
"Manager");
parser.process(app);
if (parser.positionalArguments().size())
clientInfo = readClientJson(parser.positionalArguments().first());
else
parser.showHelp();
QNetworkAccessManager manager;
QOAuth2AuthorizationCodeFlow o2(&manager, QUrl(clientInfo[redirectUriString]).port());
o2.setConsumerToken(clientInfo[clientIdString]);
o2.setAuthorizationUrl(clientInfo[authUriString]);
o2.setAccessTokenUrl(clientInfo[tokenUriString]);
o2.setScope("https://www.googleapis.com/auth/calendar.readonly");
o2.setModifyParametersFunction([=](const QAbstractOAuth::Stage stage,
QVariantMap *parameters) {
if (stage == QAbstractOAuth::Stage::RequestingAuthorization) {
}
if (stage == QAbstractOAuth::Stage::RequestingAccessToken) {
parameters->insert(clientSecretString,
QUrl::toPercentEncoding(clientInfo[clientSecretString]));
parameters->insert("grant_type", "authorization_code");
}
});
QObject::connect(&o2, &QOAuth2AuthorizationCodeFlow::authorizationRequested,
&QDesktopServices::openUrl);
QNetworkReply *reply = nullptr;
QObject::connect(&o2, &QOAuth2AuthorizationCodeFlow::granted, [&]() {
QUrl url("https://www.googleapis.com/calendar/v3/users/me/calendarList");
QNetworkRequest request(url);
reply = o2.get(request);
QObject::connect(reply, &QNetworkReply::finished, [reply]() {
auto data = reply->readAll();
qDebug() << data;
});
});
o2.grant();
return app.exec();
}
QT += network widgets
TARGET = qtgcal
# Input
SOURCES += \
main.cpp
HEADERS += \
include(../../QtOAuth.pri)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment