Google OAuth中不支持的授权类型

时间:2017-06-11 06:07:41

标签: oauth google-cloud-platform

我收到错误"不支持的授权类型"当我尝试使用curl为服务帐户请求OAuth令牌时。我关注服务帐户的OAuth 2.0示例(https://developers.google.com/identity/protocols/OAuth2ServiceAccount),我认为我已正确设置了所有内容。我在Google Cloud中设置了服务帐户,并且我在OAuth请求中使用该电子邮件地址。

文档说使用URL编码的授权类型" urn:ietf:params:oauth:grant-type:jwt-bearer"但是,如果这是授权类型的唯一选项或其他选项可能是什么,则不清楚。

我正在发送base64编码标头

{"alg":"RS256","typ":"JWT"}

和"。" 和base64编码的声明

{
  "iss":"chargepubadmin@xxxxxxxx.iam.gserviceaccount.com",
  "scope":"https://www.googleapis.com/auth/pubsub",
  "aud":"https://www.googleapis.com/oauth2/v4/token",
  "exp":1497159875,
  "iat":1497156275
}

和"。" 和base64编码签名

{base64 header}.{base64 claims}

curl -X POST -d 'grant_type=http%3A%2F%2Foauth.net%2Fgrant_type%2Fdevice%2F1.0%26assertion=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.ew0KICAiaXNzIjoiY2.......' "https://www.googleapis.com/oauth2/v4/token"

我正在使用与示例base64编码匹配的在线base64编码工具。

任何人都可以告诉我授权类型是什么或应该是什么?

1 个答案:

答案 0 :(得分:1)

应在REST API urn:ietf:params:oauth:grant-type:jwt-bearer部分下将授权类型设置为Making the access token request文档here

使用google-auth library

的工作示例

如果您使用google-auth库自动处理解析私钥json文件,获取访问令牌,刷新它们并实际将它们作为请求的一部分包含在内,那将非常简单和容易。< / p>

您只需提供请求URL和正文,图书馆负责其余部分。这是一个简化的例子:

#!/usr/bin/env python

from google.auth.transport.requests import AuthorizedSession
from google.oauth2.service_account import Credentials

# BEGIN CONFIGURATION - change as needed.
# Path to the JSON file containing the service account private key and email.
PRIVATE_KEY_JSON = '/path/to/json/file'
# The API scope this token will be valid for.
API_SCOPES = ['https://www.googleapis.com/auth/pubsub']
# END CONFIGURATION

if __name__ == '__main__':
  credentials = Credentials.from_service_account_file(
      PRIVATE_KEY_JSON, scopes=API_SCOPES)
  authed_session = AuthorizedSession(credentials)
  url = 'https://pubsub.googleapis.com/v1/<SOMETHING>'
  response = authed_session.get(url)
  print str(response.content)

没有附加库的工作示例

如果你不想使用任何额外的库,但可以使用标准的python库,这里是一个Python的工作样本(用我自己的服务帐户亲自测试)(支持2.x和3.x版本) )负责所有步骤:

#!/usr/bin/env python

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

try:
    from urllib.request import urlopen
except ImportError:
    from urllib2 import urlopen

try:
    from urllib.parse import urlencode
except ImportError:
    from urllib import urlencode


# BEGIN CONFIGURATION - change as needed.

# Path to the JSON file containing the service account private key and email.
PRIVATE_KEY_JSON = '/path/to/json/file'

# The API scope this token will be valid for.
API_SCOPE = 'https://www.googleapis.com/auth/pubsub'
# The validity of the token in seconds. Max allowed is 3600s.
ACCESS_TOKEN_VALIDITY_SECS = 3600

# END CONFIGURATION


class OauthAccessTokenGetter:
    """Fetches a new Google OAuth 2.0 access token.

    The code is based on the steps described here: https://developers.go
    ogle.com/identity/protocols/OAuth2ServiceAccount#authorizingrequests

    """

    ACCESS_TOKEN_AUD = 'https://www.googleapis.com/oauth2/v4/token'
    REQUEST_URL = 'https://www.googleapis.com/oauth2/v4/token'
    GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer'

    def __init__(self, private_key_json_file, scope, token_valid_secs=3600):
        self.private_key_json = self.LoadPrivateKeyJsonFromFile(
            private_key_json_file)
        self.scope = scope
        self.token_valid_secs = token_valid_secs

    @classmethod
    def Base64UrlEncode(cls, data):
        """Returns the base64url encoded string for the specified data."""
        return base64.urlsafe_b64encode(data)

    @classmethod
    def LoadPrivateKeyJsonFromFile(cls, private_key_json_file):
        """Returns JSON object by parsing the specified private key JSON
        file."""
        with open(private_key_json_file) as private_key_json_file:
            return json.load(private_key_json_file)

    def GetPrivateKey(self):
        """Returns the imported RSA private key from the JSON data."""
        return RSA.importKey(self.private_key_json['private_key'])

    def GetSigner(self):
        """Returns a PKCS1-V1_5 object for signing."""
        return PKCS1_v1_5.new(self.GetPrivateKey())

    @classmethod
    def GetEncodedJwtHeader(cls):
        """Returns the base64url encoded JWT header."""
        return cls.Base64UrlEncode(json.dumps({'alg': 'RS256', 'typ': 'JWT'}).encode('utf-8'))

    def GetEncodedJwtClaimSet(self):
        """Returns the base64url encoded JWT claim set."""
        current_time_secs = int(time.time())
        jwt_claims = {
            'iss': self.private_key_json['client_email'],
            'scope': self.scope,
            'aud': self.ACCESS_TOKEN_AUD,
            'exp': current_time_secs + self.token_valid_secs,
            'iat': current_time_secs
        }
        return self.Base64UrlEncode(json.dumps(jwt_claims).encode('utf-8'))

    def GetJwtSignature(self, message):
        """Returns signature of JWT as per JSON Web Signature (JWS) spec."""
        signed_message = self.GetSigner().sign(SHA.new(message))
        return self.Base64UrlEncode(signed_message)

    def GetSignedJwt(self):
        """Returns signed JWT."""
        header = self.GetEncodedJwtHeader()
        jwt_claim_set = self.GetEncodedJwtClaimSet()
        signature = self.GetJwtSignature(header + b'.' + jwt_claim_set)
        return header + b'.' + jwt_claim_set + b'.' + signature

    def SendRequest(self, body):
        """Returns the response by sending the specified request."""
        return urlopen(self.REQUEST_URL, urlencode(body).encode('utf-8')).read()

    def GetAccessToken(self):
        """Returns the access token."""
        body = {
            'grant_type': self.GRANT_TYPE,
            'assertion': self.GetSignedJwt()
        }
        response = json.loads(self.SendRequest(body))
        return response['access_token']


if __name__ == '__main__':
    print (OauthAccessTokenGetter(PRIVATE_KEY_JSON, API_SCOPE,
                                  ACCESS_TOKEN_VALIDITY_SECS).GetAccessToken())

获得访问令牌后,您需要将其作为Bearer标头包含在您发送的described here请求中。

GET /drive/v2/files HTTP/1.1
Authorization: Bearer <access_token>
Host: www.googleapis.com/

等同于卷曲:

curl -H "Authorization: Bearer <access_token>" https://www.googleapis.com/drive/v2/files

尽管described here可以使用access_token=参数指定令牌,但至少对于Google Compute Engine API我无法使用它,可能适用于PubSub,但discovery doc for PubSub API 1}}标题方法总是在我的经验中起作用。

更新根据discovery doc for Compute Engine APIsBearer似乎有一个查询参数,所以它也可以很好地运作。

access_token=

而{{3}}表示使用了"access_token": { "description": "OAuth access token.", "type": "string", "location": "query" }, 查询参数,而且我确实验证了它是否有效。

oauth_token