在谷歌云存储中使用服务帐户获取OAuth令牌时'invalid_grant'错误
'invalid_grant' error while getting OAuth token with Service Account in Google Cloud Storage
我写了一个小Qt/С++应用程序,使用谷歌云存储来保存图像文件。我在Google API控制台中创建了一个服务帐户客户端ID,并尝试与Google REST API进行交互。但在这之前,我需要得到一个OAuth授权令牌。
我一直试图得到令牌三天,但每次我得到相同的烦人的错误:{"error": "invalid_grant"}
我试图调整时间,因为它是建议在这里:invalid_grant返回使用服务帐户和谷歌驱动器API。没用。
我在谷歌API控制台创建了另一个客户端ID,如本主题所建议的:Invalid_grant在谷歌分析。没有结果。
这是我的一个请求令牌的小类的代码。谁能告诉我哪里做错了吗?
#include "googlestorageuploader.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/bio.h"
#include "openssl/err.h"
#include "openssl/pkcs12.h"
#include <QFile>
#include <QDateTime>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QSslConfiguration>
#include <QDebug>
// Just a wrapper that takes OpenSsl function 'a' and calls it.
// If 'a' returns zero the wrapper throws a string containg description
// of error occured
#define OPENSSL_FUNCTION_CALL(a)
if (!a)
{
char buffer[120];
ERR_error_string(ERR_get_error(), buffer);
char s[256];
sprintf(s, ""#a" function call failed.n%s", buffer);
throw s;
}
// String template for standard JWT Header
const QString kJwtHeader = "{"alg":"RS256","typ":"JWT"}";
// String template for JWT claim set
// Contains two placeholders for "exp" and "iat" keys
const QString kJwtClaimSet = "{"
""iss":"XXXXXXXXXXXX-yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy@developer.gserviceaccount.com","
""scope":"https://www.googleapis.com/auth/devstorage.readonly","
""aud":"https://accounts.google.com/o/oauth2/token","
""exp":%1,"
""iat":%2"
"}";
// This function computes JWT signature
// 'input' is an array of bytes passed as an input
// 'privateKey' is an array of bytes that contains a private key in PKCS12 format
// (as it is in the file received from 'Google APIs Console->API Access')
// Uses OpenSSL API to create a signature using SHA256withRSA
QByteArray signWithRsaSha256(const QByteArray &input, const QByteArray &privateKey)
{
EVP_PKEY *pkey = 0;
BIO *bp = 0;
EVP_MD_CTX *ctx = 0;
const EVP_MD *sha256Md = 0;
unsigned char sig[256];
unsigned int s(0);
QByteArray out;
PKCS12 *p12 = 0;
X509 *cert= 0;
OpenSSL_add_all_ciphers();
OpenSSL_add_all_digests();
ctx = EVP_MD_CTX_create();
EVP_MD_CTX_init(ctx);
sha256Md = EVP_sha256(); // TODO: need to free
bp = BIO_new_mem_buf((void*)privateKey.data(), privateKey.size());
try {
OPENSSL_FUNCTION_CALL(EVP_SignInit(ctx, sha256Md));
OPENSSL_FUNCTION_CALL(EVP_SignUpdate(ctx, input.constData(), input.size()));
OPENSSL_FUNCTION_CALL(d2i_PKCS12_bio(bp, &p12));
OPENSSL_FUNCTION_CALL(PKCS12_parse(p12, "notasecret", &pkey, &cert, NULL));
s = EVP_PKEY_size(pkey);
OPENSSL_FUNCTION_CALL(EVP_SignFinal(ctx, sig, &s, pkey));
out.setRawData((const char *)sig, s);
}
catch (const char *s)
{
qCritical() << s;
}
EVP_MD_CTX_destroy(ctx);
BIO_free(bp);
X509_free(cert);
EVP_cleanup();
return out;
}
CGoogleStorageUploader::CGoogleStorageUploader(QObject *parent) :
QObject(parent)
{
nam_ = new QNetworkAccessManager(this);
}
// Creates and sends HTTP POST request to get an authorization token
void CGoogleStorageUploader::requestToken()
{
QNetworkRequest req(QUrl("https://accounts.google.com/o/oauth2/token"));
req.setRawHeader("Host", "accounts.google.com");
req.setRawHeader("Content-Type", "application/x-www-form-urlencoded");
req.setSslConfiguration(QSslConfiguration::defaultConfiguration());
QByteArray assertion = formJWT();
QByteArray data;
data = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + assertion;
QNetworkReply *reply = nam_->post(req, data);
connect(reply, SIGNAL(finished()), SLOT(onNetworkReplyFinished()));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)), SLOT(onNetworkReplySslErrors(QList<QSslError>)));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(onNetworkReplyError(QNetworkReply::NetworkError)));
}
// Constructs JWT
QByteArray CGoogleStorageUploader::formJWT()
{
QDateTime now = QDateTime::currentDateTime();
QDateTime utcNow = now.toUTC();
quint32 secs = utcNow.toTime_t() - 300; // adjust to Google Server time
QString claimSet = kJwtClaimSet.arg(secs + 3600).arg(secs); // fill "exp" and "iat" fields
QByteArray signature, privateKey, jwt;
QFile file (":res/pk.p12"); // copy of file received from Google API Console
if (file.open(QFile::ReadOnly))
{
privateKey = file.readAll();
QByteArray encodedHeader = kJwtHeader.toUtf8().toBase64(); // serialize to UTF8 and Base64url safe encode (https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset)
QByteArray encodedClaimSet = claimSet.toUtf8().toBase64(); // serialize to UTF8 and Base64url safe encode (https://developers.google.com/accounts/docs/OAuth2ServiceAccount#formingclaimset)
// "Sign the UTF-8 representation of the input" (https://developers.google.com/accounts/docs/OAuth2ServiceAccount#computingsignature)
signature = signWithRsaSha256(QString(encodedHeader + "." + encodedClaimSet).toUtf8(), privateKey);
QByteArray encodedSignature = signature.toBase64();
jwt = encodedHeader + "." + encodedClaimSet + "." + encodedSignature;
}
file.close();
qDebug() << jwt;
return jwt;
}
void CGoogleStorageUploader::onNetworkReplyFinished()
{
QNetworkReply *r = static_cast<QNetworkReply*>(sender());
qDebug() << "finished: " << r->readAll();
}
void CGoogleStorageUploader::onNetworkReplySslErrors(const QList<QSslError> &errs)
{
QNetworkReply *r = static_cast<QNetworkReply*>(sender());
qDebug() << "ssl errors: " << r->readAll();
r->ignoreSslErrors(errs);
}
void CGoogleStorageUploader::onNetworkReplyError(QNetworkReply::NetworkError err)
{
qDebug() << "network error: " << err;
}
你不需要先打个电话来获取代码吗?有了代码之后,就可以调用获取令牌了…
相关文章:
- 如何在C++中使用X509证书模在令牌中查找私钥
- holeMenuProgram.cpp:38:1 错误:'}'令牌之前的预期主表达式
- C++:"("令牌"之前有预期的非限定 id 指向类中成员函数的指针
- Arduino 用于语句错误。令牌之前的预期')' ';'。如何解决这个问题?
- 错误:令牌 { '{' 之前应存在非限定 ID
- 允许哪些令牌作为 #include 的参数?
- "错误 C0000:语法错误,令牌"<EOF>"处出现意外$end,并且不确定
- 它在 { 令牌之前给了我预期的标识符
- (错误:令牌之前'<<'预期的主表达式)
- 为什么 ## aka 令牌粘贴运算符不适用于 C 和 C++ 中的注释?
- 错误:"->"令牌之前的预期初始值设定项
- LINUX 操作系统上的错误:令牌之前预期的构造函数、析构函数或类型转换'('?
- 丢失读入的每一行输入中的最后一个令牌
- 如何使用 libCurl 将访问令牌发送到服务器 API
- C++令牌定义成员
- 宏定义中的预处理器令牌两边有两个双引号
- 错误:C++中']'令牌之前的预期主表达式
- 错误:令牌之前的预期主表达式'&&'
- 令牌之前']'预期的主表达式?
- 无法在硬件模式下创建 SGX 安全区 - "invalid launch token"即使文档将无效的启动令牌指定为第一个