使用oauth2服务帐户在python中对Google API进行身份验证

使用oauth2服务帐户在python中对Google API进行身份验证

问题描述:

我已按照 https://developers.google.com/accounts/docs中的说明进行操作/OAuth2ServiceAccount 以使用服务帐户向Google Cloud Storage API进行身份验证.我尝试将JWT发送给python的Google身份验证服务器,但收到错误消息:

I've followed the directions in https://developers.google.com/accounts/docs/OAuth2ServiceAccount to use a service account to authenticate to the Google Cloud Storage API. I tried to send a JWT to google's authenticate servers in python, but got an error:

urllib2.HTTPError: HTTP Error 400: Bad Request

我制作,签名或发送JWT的方式似乎有问题吗?错误不是特定的,因此可能是过程的任何部分.有人有什么想法吗?

It looks like there's something wrong with the way I'm making, signing, or sending the JWT? The error wasn't specific so it could be any part of the process. Does anyone have any ideas?

import Crypto.PublicKey.RSA as RSA
import Crypto.Hash.SHA as SHA
import Crypto.Signature.PKCS1_v1_5 as PKCS1_v1_5
import base64
import json
import time
import urllib2
import urllib

# Settings
json_key_file = 'GooglePM-9f75ad112f87-service.json'

# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
    json_data = json.load(json_file)
    key = RSA.importKey(json_data['private_key'])

# Create an PKCS1_v1_5 object
signer = PKCS1_v1_5.new(key)

# Encode the JWT header
header_b64 = base64.urlsafe_b64encode(json.dumps({'alg':'RS256','typ':'JWT'}))

# JWT claims
jwt = {
    'iss': json_data['client_email'],
    'scope': 'https://www.googleapis.com/auth/devstorage.read_write',
    'aud': 'https://accounts.google.com/o/oauth2/token',
    'exp': int(time.time())+3600,
    'iat': int(time.time())
    }
jwt_json = json.dumps(jwt)

# Encode the JWT claims
jwt_json_b64 = base64.urlsafe_b64encode(jwt_json)

# Sign the JWT header and claims
msg_hash = SHA.new(header_b64 + "." + jwt_json_b64)
signature_b64 = base64.urlsafe_b64encode(signer.sign(msg_hash))

# Make the complete message
jwt_complete = header_b64 + "." + jwt_json_b64 + "." + signature_b64

data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
    'assertion': jwt_complete}

f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))

print f.read()

如果我尝试使用curl将其发布到服务器,则会收到无效的赠款错误:

If I try to use curl to post to the server, I get the invalid grants error:

(venv)$ curl -d 'grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=eyJhbGciOiAiUlMyNTYiLCAidHlwIjogIkpXVCJ9.eyJpc3MiOiAiMTM1MDY3NjIyMTk4LWVhbWUwZnFqdTNvamRoZ29zdDg2dnBpdTBsYW91NnZlQGRldmVsb3Blci5nc2VydmljZWFjY291bnQuY29tIiwgInNjb3BlIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL2F1dGgvZGV2c3RvcmFnZS5yZWFkX3dyaXRlIiwgImF1ZCI6ICJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20vby9vYXV0aDIvdG9rZW4iLCAiZXhwIjogMTQwODY1MTU2OCwgImlhdCI6IDE0MDg2NDg1NTh9.HWC7h3QiOy7QsSuta4leq_Gjwmy9IdF-MUwflPhiohzAJ-Amykd56Ye4Y_Saf_sAc5STzOCmrSPzOTYvGXr6X_T_AmSTxXK2AJ2SpAiEUs2_Wp5h18xTUY3Y_hkKvSZLh5bRzeJ_0xRcmRIPE6tua0FHFwUDdnCIGdh4DGg6i4E%3D' https://accounts.google.com/o/oauth2/token
{
  "error" : "invalid_grant"
}

好的,所以有一种更好的方法! Google已经拥有处理某些复杂性的python客户端API.安装Google python客户端API后,以下代码将起作用: https://developers.google.com/api -client-library/python/guide/aaa_oauth

Ok so there's a better way to do this! Google already has a python client API that handles some of the complexity. The following code works after installing google python client API: https://developers.google.com/api-client-library/python/guide/aaa_oauth

from oauth2client.client import SignedJwtAssertionCredentials
import json
import urllib
import urllib2

# Settings
json_key_file = 'GooglePM-9f75ad112f87-service.json'

# Load the private key associated with the Google service account
with open(json_key_file) as json_file:
    json_data = json.load(json_file)

# Get and sign JWT
credential = SignedJwtAssertionCredentials(json_data['client_email'], json_data['private_key'], 'https://www.googleapis.com/auth/devstorage.read_write')
jwt_complete = credential._generate_assertion()

# Get token from server
data = {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 
    'assertion': jwt_complete}
f = urllib2.urlopen("https://accounts.google.com/o/oauth2/token", urllib.urlencode(data))

print f.read()