2013-03-17 129 views
1

我已经编写了一个使用Google云端存储来保存图像文件的小型Qt /С++应用。我在我的Google API控制台中创建了一个服务帐户客户端ID,并尝试与Google REST API进行交互。但在此之前我需要获得OAuth授权令牌。在Google云端存储中获取具有服务帐户的OAuth令牌时发生'invalid_grant'错误

我一直在试图获得令牌三天,但每次我得到同样的错误烦人: { “错误”:“invalid_grant” }

我试图调整时间,因为它是这里建议:invalid_grant Returned using Service Account and Google Drive API。它没有帮助。

我在Google API Console中创建了另一个客户端ID,如本主题所示:Invalid_grant in Google Analytics。没有结果。

这是我的小小的代码请求令牌。请任何人都可以说我做错了什么?

#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\":\"[email protected]account.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; 
} 

回答

-1

难道你不得不先拨打电话才能拿到密码吗? 你有代码后,你可以打电话获取令牌...

+0

对不起,我通过拨打电话先得到什么代码? 我正在使用服务帐户获取OAuth访问令牌,因为它在此处描述了[link](https://developers.google.com/accounts/docs/OAuth2ServiceAccount) – pehcha 2013-03-18 08:02:46

相关问题