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 = int(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 assertion_input = assertion_input.encode('utf-8')
81
82
83 signature = bytes.decode(base64.urlsafe_b64encode(rsa.pkcs1.sign(
84 assertion_input, self._private_key, 'SHA-256'))).rstrip('=')
85
86 return '%s.%s' % (assertion_input, signature)
87
89
90 try:
91 blob = blob.encode('utf-8')
92 except AttributeError:
93 pass
94 return (self._private_key_id,
95 rsa.pkcs1.sign(blob, self._private_key, 'SHA-256'))
96
97 @property
99 return self._service_account_email
100
101 @property
103 return {
104 'type': 'service_account',
105 'client_id': self._service_account_id,
106 'client_email': self._service_account_email,
107 'private_key_id': self._private_key_id,
108 'private_key': self._private_key_pkcs8_text
109 }
110
112 return not self._scopes
113
115 return _ServiceAccountCredentials(self._service_account_id,
116 self._service_account_email,
117 self._private_key_id,
118 self._private_key_pkcs8_text,
119 scopes,
120 user_agent=self._user_agent,
121 token_uri=self._token_uri,
122 revoke_uri=self._revoke_uri,
123 **self._kwargs)
124
127 return base64.urlsafe_b64encode(
128 json.dumps(data, separators=(',', ':')).encode('UTF-8')).rstrip(b'=')
129
132 """Get an RSA private key object from a pkcs8 representation."""
133
134 der = rsa.pem.load_pem(private_key_pkcs8_text, 'PRIVATE KEY')
135 asn1_private_key, _ = decoder.decode(der, asn1Spec=PrivateKeyInfo())
136 return rsa.PrivateKey.load_pkcs1(
137 asn1_private_key.getComponentByName('privateKey').asOctets(),
138 format='DER')
139