1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 """A service account credentials class.
16
17 This credentials class is implemented on top of rsa library.
18 """
19
20 import base64
21 import json
22 import time
23
24 from pyasn1.codec.ber import decoder
25 from pyasn1_modules.rfc5208 import PrivateKeyInfo
26 import rsa
27
28 from oauth2client import GOOGLE_REVOKE_URI
29 from oauth2client import GOOGLE_TOKEN_URI
30 from oauth2client import util
31 from oauth2client.client import AssertionCredentials
35 """Class representing a service account (signed JWT) credential."""
36
37 MAX_TOKEN_LIFETIME_SECS = 3600
38
43
44 super(_ServiceAccountCredentials, self).__init__(
45 None, user_agent=user_agent, token_uri=token_uri, revoke_uri=revoke_uri)
46
47 self._service_account_id = service_account_id
48 self._service_account_email = service_account_email
49 self._private_key_id = private_key_id
50 self._private_key = _get_private_key(private_key_pkcs8_text)
51 self._private_key_pkcs8_text = private_key_pkcs8_text
52 self._scopes = util.scopes_to_string(scopes)
53 self._user_agent = user_agent
54 self._token_uri = token_uri
55 self._revoke_uri = revoke_uri
56 self._kwargs = kwargs
57
59 """Generate the assertion that will be used in the request."""
60
61 header = {
62 'alg': 'RS256',
63 'typ': 'JWT',
64 'kid': self._private_key_id
65 }
66
67 now = long(time.time())
68 payload = {
69 'aud': self._token_uri,
70 'scope': self._scopes,
71 'iat': now,
72 'exp': now + _ServiceAccountCredentials.MAX_TOKEN_LIFETIME_SECS,
73 'iss': self._service_account_email
74 }
75 payload.update(self._kwargs)
76
77 assertion_input = '%s.%s' % (
78 _urlsafe_b64encode(header),
79 _urlsafe_b64encode(payload))
80
81
82 signature = base64.urlsafe_b64encode(rsa.pkcs1.sign(
83 assertion_input, self._private_key, 'SHA-256')).rstrip('=')
84
85 return '%s.%s' % (assertion_input, signature)
86
88 return (self._private_key_id,
89 rsa.pkcs1.sign(blob, self._private_key, 'SHA-256'))
90
91 @property
93 return self._service_account_email
94
95 @property
97 return {
98 'type': 'service_account',
99 'client_id': self._service_account_id,
100 'client_email': self._service_account_email,
101 'private_key_id': self._private_key_id,
102 'private_key': self._private_key_pkcs8_text
103 }
104
106 return not self._scopes
107
109 return _ServiceAccountCredentials(self._service_account_id,
110 self._service_account_email,
111 self._private_key_id,
112 self._private_key_pkcs8_text,
113 scopes,
114 user_agent=self._user_agent,
115 token_uri=self._token_uri,
116 revoke_uri=self._revoke_uri,
117 **self._kwargs)
118
121 return base64.urlsafe_b64encode(
122 json.dumps(data, separators=(',', ':')).encode('UTF-8')).rstrip('=')
123
126 """Get an RSA private key object from a pkcs8 representation."""
127
128 der = rsa.pem.load_pem(private_key_pkcs8_text, 'PRIVATE KEY')
129 asn1_private_key, _ = decoder.decode(der, asn1Spec=PrivateKeyInfo())
130 return rsa.PrivateKey.load_pkcs1(
131 asn1_private_key.getComponentByName('privateKey').asOctets(),
132 format='DER')
133