|
1 | 1 | #include "googlecloudoauth2.h"
|
2 | 2 |
|
| 3 | +#include "jwt-cpp/jwt.h" |
| 4 | + |
3 | 5 | #include <QFile>
|
4 | 6 | #include <QJsonDocument>
|
5 | 7 | #include <QJsonObject>
|
| 8 | +#include <QLoggingCategory> |
6 | 9 | #include <QNetworkReply>
|
7 | 10 | #include <QNetworkRequest>
|
8 | 11 | #include <QUrl>
|
9 | 12 | #include <QUrlQuery>
|
10 | 13 |
|
11 |
| -#include <QLoggingCategory> |
12 |
| - |
13 |
| -#include "jwt-cpp/jwt.h" |
14 |
| - |
15 | 14 | Q_LOGGING_CATEGORY(GC_OAUTH, "gc.oauth", QtInfoMsg)
|
16 | 15 |
|
17 | 16 | using namespace Qt::StringLiterals;
|
18 | 17 |
|
19 | 18 | GoogleCloudOAuth2::GoogleCloudOAuth2(QObject *parent)
|
20 |
| - : QObject(parent), m_nam(new QNetworkAccessManager(this)) {} |
21 |
| - |
22 |
| -void GoogleCloudOAuth2::setAccountCredentialFile(const QString &filename) { |
23 |
| - QFile file(filename); |
24 |
| - if (file.open(QFile::ReadOnly)) { |
25 |
| - setAccountCredentialData(file.readAll()); |
26 |
| - } else { |
27 |
| - qCWarning(GC_OAUTH) << "Failed to open credentials file" << filename |
28 |
| - << file.errorString(); |
29 |
| - } |
| 19 | + : QObject(parent) |
| 20 | + , m_nam(new QNetworkAccessManager(this)) |
| 21 | +{ |
30 | 22 | }
|
31 | 23 |
|
32 |
| -void GoogleCloudOAuth2::setAccountCredentialData(const QByteArray &data) { |
33 |
| - QJsonParseError error; |
34 |
| - QJsonDocument doc = QJsonDocument::fromJson(data, &error); |
35 |
| - if (!error.error) { |
36 |
| - setAccountCredential(doc.object()); |
37 |
| - } else { |
38 |
| - qCWarning(GC_OAUTH) << "Failed to parse credentials data" |
39 |
| - << error.errorString(); |
40 |
| - } |
| 24 | +void GoogleCloudOAuth2::setAccountCredentialFile(const QString &filename) |
| 25 | +{ |
| 26 | + QFile file(filename); |
| 27 | + if (file.open(QFile::ReadOnly)) { |
| 28 | + setAccountCredentialData(file.readAll()); |
| 29 | + } else { |
| 30 | + qCWarning(GC_OAUTH) << "Failed to open credentials file" << filename << file.errorString(); |
| 31 | + } |
41 | 32 | }
|
42 | 33 |
|
43 |
| -void GoogleCloudOAuth2::setAccountCredential( |
44 |
| - const QJsonObject &accountCredential) { |
45 |
| - m_credentialsFile = accountCredential; |
| 34 | +void GoogleCloudOAuth2::setAccountCredentialData(const QByteArray &data) |
| 35 | +{ |
| 36 | + QJsonParseError error; |
| 37 | + QJsonDocument doc = QJsonDocument::fromJson(data, &error); |
| 38 | + if (!error.error) { |
| 39 | + setAccountCredential(doc.object()); |
| 40 | + } else { |
| 41 | + qCWarning(GC_OAUTH) << "Failed to parse credentials data" << error.errorString(); |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +void GoogleCloudOAuth2::setAccountCredential(const QJsonObject &accountCredential) |
| 46 | +{ |
| 47 | + m_credentialsFile = accountCredential; |
46 | 48 | }
|
47 | 49 |
|
48 |
| -QJsonObject GoogleCloudOAuth2::accountCredential() const { |
49 |
| - return m_credentialsFile; |
| 50 | +QJsonObject GoogleCloudOAuth2::accountCredential() const |
| 51 | +{ |
| 52 | + return m_credentialsFile; |
50 | 53 | }
|
51 | 54 |
|
52 |
| -QNetworkAccessManager *GoogleCloudOAuth2::networkAccessManager() const { |
53 |
| - return m_nam; |
| 55 | +QNetworkAccessManager *GoogleCloudOAuth2::networkAccessManager() const |
| 56 | +{ |
| 57 | + return m_nam; |
54 | 58 | }
|
55 | 59 |
|
56 |
| -void GoogleCloudOAuth2::setNetworkAccessManager(QNetworkAccessManager *nam) { |
57 |
| - if (m_nam) { |
58 |
| - delete m_nam; |
59 |
| - } |
60 |
| - m_nam = nam; |
| 60 | +void GoogleCloudOAuth2::setNetworkAccessManager(QNetworkAccessManager *nam) |
| 61 | +{ |
| 62 | + if (m_nam) { |
| 63 | + delete m_nam; |
| 64 | + } |
| 65 | + m_nam = nam; |
61 | 66 | }
|
62 | 67 |
|
63 |
| -QNetworkRequest GoogleCloudOAuth2::defaultRequest(const QUrl &url) const { |
64 |
| - QNetworkRequest req(url); |
65 |
| - req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); |
66 |
| - // req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); // Not |
67 |
| - // yet Google? |
68 |
| - req.setRawHeader(QByteArrayLiteral("AUTHORIZATION"), m_accessTokenHeader); |
| 68 | +QNetworkRequest GoogleCloudOAuth2::defaultRequest(const QUrl &url) const |
| 69 | +{ |
| 70 | + QNetworkRequest req(url); |
| 71 | + req.setAttribute(QNetworkRequest::HttpPipeliningAllowedAttribute, true); |
| 72 | + // req.setAttribute(QNetworkRequest::HTTP2AllowedAttribute, true); // Not |
| 73 | + // yet Google? |
| 74 | + req.setRawHeader("AUTHORIZATION"_ba, m_accessTokenHeader); |
69 | 75 |
|
70 |
| - return req; |
| 76 | + return req; |
71 | 77 | }
|
72 | 78 |
|
73 |
| -QByteArray GoogleCloudOAuth2::accessTokenHeader() const { |
74 |
| - return m_accessTokenHeader; |
| 79 | +QByteArray GoogleCloudOAuth2::accessTokenHeader() const |
| 80 | +{ |
| 81 | + return m_accessTokenHeader; |
75 | 82 | }
|
76 | 83 |
|
77 |
| -void GoogleCloudOAuth2::setScopes(const QStringList &scopes) { |
78 |
| - m_scopes = scopes; |
| 84 | +void GoogleCloudOAuth2::setScopes(const QStringList &scopes) |
| 85 | +{ |
| 86 | + m_scopes = scopes; |
79 | 87 | }
|
80 | 88 |
|
81 |
| -void GoogleCloudOAuth2::getAccessToken() { |
82 |
| - const QString tokenUri = m_credentialsFile[u"token_uri"].toString(); |
83 |
| - const QString privateKey = m_credentialsFile[u"private_key"].toString(); |
84 |
| - |
85 |
| - try { |
86 |
| - auto algo = jwt::algorithm::rs256{std::string(), privateKey.toStdString()}; |
87 |
| - |
88 |
| - auto token = |
89 |
| - jwt::create() |
90 |
| - .set_type("JWT") |
91 |
| - .set_issuer( |
92 |
| - m_credentialsFile[u"client_email"].toString().toStdString()) |
93 |
| - .set_payload_claim("scope", |
94 |
| - jwt::claim(m_scopes.join(u' ').toStdString())) |
95 |
| - .set_audience(tokenUri.toStdString()) |
96 |
| - .set_issued_at(std::chrono::system_clock::now()) |
97 |
| - .set_expires_at(std::chrono::system_clock::now() + |
98 |
| - std::chrono::seconds{3600}) |
99 |
| - .sign(algo); |
100 |
| - |
101 |
| - QUrlQuery query; |
102 |
| - query.addQueryItem(u"grant_type"_s, |
103 |
| - u"urn:ietf:params:oauth:grant-type:jwt-bearer"_s); |
104 |
| - query.addQueryItem(u"assertion"_s, QString::fromStdString(token)); |
105 |
| - |
106 |
| - QNetworkRequest req(QUrl{tokenUri}); |
107 |
| - req.setHeader(QNetworkRequest::ContentTypeHeader, |
108 |
| - "application/x-www-form-urlencoded"_ba); |
109 |
| - |
110 |
| - QNetworkReply *reply = |
111 |
| - m_nam->post(req, query.toString(QUrl::FullyEncoded).toLatin1()); |
112 |
| - connect(reply, &QNetworkReply::finished, this, [=, this] { |
113 |
| - reply->deleteLater(); |
114 |
| - const QByteArray data = reply->readAll(); |
115 |
| - m_token = QJsonObject(); |
116 |
| - m_accessTokenHeader.clear(); |
117 |
| - GoogleCloudReply gcr; |
118 |
| - |
119 |
| - if (!reply->error()) { |
120 |
| - QJsonParseError error; |
121 |
| - QJsonDocument doc = QJsonDocument::fromJson(data, &error); |
122 |
| - if (!error.error) { |
123 |
| - m_token = doc.object(); |
124 |
| - m_accessTokenHeader = QByteArrayLiteral("Bearer ") + |
125 |
| - m_token.value(QStringLiteral("access_token")) |
126 |
| - .toString() |
127 |
| - .toLatin1(); |
128 |
| - m_expires = |
129 |
| - m_token.value(QLatin1String("expires_in")).toInt() * 1000 - |
130 |
| - 30'000; |
131 |
| - m_expiresTimer.start(); |
132 |
| - qCDebug(GC_OAUTH) << "Got Access Token" << m_token; |
133 |
| - } else { |
134 |
| - gcr.error = true; |
135 |
| - qCWarning(GC_OAUTH) << "Failed to parse google token file" << data |
136 |
| - << error.errorString(); |
137 |
| - } |
138 |
| - } else { |
| 89 | +void GoogleCloudOAuth2::getAccessToken() |
| 90 | +{ |
| 91 | + const QString tokenUri = m_credentialsFile[u"token_uri"].toString(); |
| 92 | + const QString privateKey = m_credentialsFile[u"private_key"].toString(); |
| 93 | + |
| 94 | + try { |
| 95 | + auto algo = jwt::algorithm::rs256{std::string(), privateKey.toStdString()}; |
| 96 | + |
| 97 | + auto token = |
| 98 | + jwt::create() |
| 99 | + .set_type("JWT") |
| 100 | + .set_issuer(m_credentialsFile[u"client_email"].toString().toStdString()) |
| 101 | + .set_payload_claim("scope", jwt::claim(m_scopes.join(u' ').toStdString())) |
| 102 | + .set_audience(tokenUri.toStdString()) |
| 103 | + .set_issued_at(std::chrono::system_clock::now()) |
| 104 | + .set_expires_at(std::chrono::system_clock::now() + std::chrono::seconds{3600}) |
| 105 | + .sign(algo); |
| 106 | + |
| 107 | + QUrlQuery query; |
| 108 | + query.addQueryItem(u"grant_type"_s, u"urn:ietf:params:oauth:grant-type:jwt-bearer"_s); |
| 109 | + query.addQueryItem(u"assertion"_s, QString::fromStdString(token)); |
| 110 | + |
| 111 | + QNetworkRequest req(QUrl{tokenUri}); |
| 112 | + req.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"_ba); |
| 113 | + |
| 114 | + QNetworkReply *reply = m_nam->post(req, query.toString(QUrl::FullyEncoded).toLatin1()); |
| 115 | + connect(reply, &QNetworkReply::finished, this, [=, this] { |
| 116 | + reply->deleteLater(); |
| 117 | + const QByteArray data = reply->readAll(); |
| 118 | + m_token = QJsonObject(); |
| 119 | + m_accessTokenHeader.clear(); |
| 120 | + GoogleCloudReply gcr; |
| 121 | + |
| 122 | + if (!reply->error()) { |
| 123 | + QJsonParseError error; |
| 124 | + QJsonDocument doc = QJsonDocument::fromJson(data, &error); |
| 125 | + if (!error.error) { |
| 126 | + m_token = doc.object(); |
| 127 | + m_accessTokenHeader = |
| 128 | + "Bearer "_ba + m_token.value(u"access_token").toString().toLatin1(); |
| 129 | + m_expires = m_token.value(u"expires_in").toInt() * 1000 - 30'000; |
| 130 | + m_expiresTimer.start(); |
| 131 | + qCDebug(GC_OAUTH) << "Got Access Token" << m_token; |
| 132 | + } else { |
| 133 | + gcr.error = true; |
| 134 | + qCWarning(GC_OAUTH) |
| 135 | + << "Failed to parse google token file" << data << error.errorString(); |
| 136 | + } |
| 137 | + } else { |
| 138 | + gcr.error = true; |
| 139 | + qCWarning(GC_OAUTH) << "Get Access Token failed" << reply->error(); |
| 140 | + } |
| 141 | + |
| 142 | + for (const auto &code : m_codeToRun) { |
| 143 | + if (code.receiver) { |
| 144 | + code.code(gcr); |
| 145 | + } |
| 146 | + } |
| 147 | + m_codeToRun.clear(); |
| 148 | + |
| 149 | + m_gettingToken = false; |
| 150 | + }); |
| 151 | + m_gettingToken = true; |
| 152 | + } catch (const jwt::error::rsa_exception &e) { |
| 153 | + qCDebug(GC_OAUTH) << "FAILED RSA:" << e.what(); |
| 154 | + GoogleCloudReply gcr; |
139 | 155 | gcr.error = true;
|
140 |
| - qCWarning(GC_OAUTH) << "Get Access Token failed" << reply->error(); |
141 |
| - } |
142 |
| - |
143 |
| - for (const auto &code : m_codeToRun) { |
144 |
| - if (code.receiver) { |
145 |
| - code.code(gcr); |
| 156 | + for (const auto &code : m_codeToRun) { |
| 157 | + if (code.receiver) { |
| 158 | + code.code(gcr); |
| 159 | + } |
146 | 160 | }
|
147 |
| - } |
148 |
| - m_codeToRun.clear(); |
149 |
| - |
150 |
| - m_gettingToken = false; |
151 |
| - }); |
152 |
| - m_gettingToken = true; |
153 |
| - } catch (const jwt::error::rsa_exception &e) { |
154 |
| - qCDebug(GC_OAUTH) << "FAILED RSA:" << e.what(); |
155 |
| - GoogleCloudReply gcr; |
156 |
| - gcr.error = true; |
157 |
| - for (const auto &code : m_codeToRun) { |
158 |
| - if (code.receiver) { |
159 |
| - code.code(gcr); |
160 |
| - } |
| 161 | + m_codeToRun.clear(); |
161 | 162 | }
|
162 |
| - m_codeToRun.clear(); |
163 |
| - } |
164 | 163 | }
|
165 | 164 |
|
166 |
| -void GoogleCloudOAuth2::getAccessToken( |
167 |
| - const QObject *receiver, |
168 |
| - std::function<void(const GoogleCloudReply &)> code) { |
169 |
| - if (!m_token.isEmpty() && !m_expiresTimer.hasExpired(m_expires)) { |
170 |
| - code(GoogleCloudReply()); |
171 |
| - } else { |
172 |
| - m_codeToRun.push_back(GoogleCloudCode{receiver, code}); |
173 |
| - if (!m_gettingToken) { |
174 |
| - getAccessToken(); |
| 165 | +void GoogleCloudOAuth2::getAccessToken(const QObject *receiver, |
| 166 | + std::function<void(const GoogleCloudReply &)> code) |
| 167 | +{ |
| 168 | + if (!m_token.isEmpty() && !m_expiresTimer.hasExpired(m_expires)) { |
| 169 | + code(GoogleCloudReply()); |
| 170 | + } else { |
| 171 | + m_codeToRun.push_back(GoogleCloudCode{receiver, code}); |
| 172 | + if (!m_gettingToken) { |
| 173 | + getAccessToken(); |
| 174 | + } |
175 | 175 | }
|
176 |
| - } |
177 | 176 | }
|
0 commit comments