3535from google .auth import jwt
3636
3737_URLENCODED_CONTENT_TYPE = "application/x-www-form-urlencoded"
38+ _JSON_CONTENT_TYPE = "application/json"
3839_JWT_GRANT_TYPE = "urn:ietf:params:oauth:grant-type:jwt-bearer"
3940_REFRESH_GRANT_TYPE = "refresh_token"
4041
4142
42- def _handle_error_response (response_body ):
43- """" Translates an error response into an exception.
43+ def _handle_error_response (response_data ):
44+ """Translates an error response into an exception.
4445
4546 Args:
46- response_body (str ): The decoded response data.
47+ response_data (Mapping ): The decoded response data.
4748
4849 Raises:
49- google.auth.exceptions.RefreshError
50+ google.auth.exceptions.RefreshError: The errors contained in response_data.
5051 """
5152 try :
52- error_data = json .loads (response_body )
5353 error_details = "{}: {}" .format (
54- error_data ["error" ], error_data .get ("error_description" )
54+ response_data ["error" ], response_data .get ("error_description" )
5555 )
5656 # If no details could be extracted, use the response data.
5757 except (KeyError , ValueError ):
58- error_details = response_body
58+ error_details = json . dumps ( response_data )
5959
60- raise exceptions .RefreshError (error_details , response_body )
60+ raise exceptions .RefreshError (error_details , response_data )
6161
6262
6363def _parse_expiry (response_data ):
@@ -78,25 +78,35 @@ def _parse_expiry(response_data):
7878 return None
7979
8080
81- def _token_endpoint_request (request , token_uri , body ):
81+ def _token_endpoint_request_no_throw (
82+ request , token_uri , body , access_token = None , use_json = False
83+ ):
8284 """Makes a request to the OAuth 2.0 authorization server's token endpoint.
85+ This function doesn't throw on response errors.
8386
8487 Args:
8588 request (google.auth.transport.Request): A callable used to make
8689 HTTP requests.
8790 token_uri (str): The OAuth 2.0 authorizations server's token endpoint
8891 URI.
8992 body (Mapping[str, str]): The parameters to send in the request body.
93+ access_token (Optional(str)): The access token needed to make the request.
94+ use_json (Optional(bool)): Use urlencoded format or json format for the
95+ content type. The default value is False.
9096
9197 Returns:
92- Mapping[str, str]: The JSON-decoded response data.
93-
94- Raises:
95- google.auth.exceptions.RefreshError: If the token endpoint returned
96- an error.
98+ Tuple(bool, Mapping[str, str]): A boolean indicating if the request is
99+ successful, and a mapping for the JSON-decoded response data.
97100 """
98- body = urllib .parse .urlencode (body ).encode ("utf-8" )
99- headers = {"content-type" : _URLENCODED_CONTENT_TYPE }
101+ if use_json :
102+ headers = {"Content-Type" : _JSON_CONTENT_TYPE }
103+ body = json .dumps (body ).encode ("utf-8" )
104+ else :
105+ headers = {"Content-Type" : _URLENCODED_CONTENT_TYPE }
106+ body = urllib .parse .urlencode (body ).encode ("utf-8" )
107+
108+ if access_token :
109+ headers ["Authorization" ] = "Bearer {}" .format (access_token )
100110
101111 retry = 0
102112 # retry to fetch token for maximum of two times if any internal failure
@@ -121,8 +131,38 @@ def _token_endpoint_request(request, token_uri, body):
121131 ):
122132 retry += 1
123133 continue
124- _handle_error_response (response_body )
134+ return response .status == http_client .OK , response_data
135+
136+ return response .status == http_client .OK , response_data
137+
138+
139+ def _token_endpoint_request (
140+ request , token_uri , body , access_token = None , use_json = False
141+ ):
142+ """Makes a request to the OAuth 2.0 authorization server's token endpoint.
143+
144+ Args:
145+ request (google.auth.transport.Request): A callable used to make
146+ HTTP requests.
147+ token_uri (str): The OAuth 2.0 authorizations server's token endpoint
148+ URI.
149+ body (Mapping[str, str]): The parameters to send in the request body.
150+ access_token (Optional(str)): The access token needed to make the request.
151+ use_json (Optional(bool)): Use urlencoded format or json format for the
152+ content type. The default value is False.
153+
154+ Returns:
155+ Mapping[str, str]: The JSON-decoded response data.
125156
157+ Raises:
158+ google.auth.exceptions.RefreshError: If the token endpoint returned
159+ an error.
160+ """
161+ response_status_ok , response_data = _token_endpoint_request_no_throw (
162+ request , token_uri , body , access_token = access_token , use_json = use_json
163+ )
164+ if not response_status_ok :
165+ _handle_error_response (response_data )
126166 return response_data
127167
128168
@@ -204,8 +244,43 @@ def id_token_jwt_grant(request, token_uri, assertion):
204244 return id_token , expiry , response_data
205245
206246
247+ def _handle_refresh_grant_response (response_data , refresh_token ):
248+ """Extract tokens from refresh grant response.
249+
250+ Args:
251+ response_data (Mapping[str, str]): Refresh grant response data.
252+ refresh_token (str): Current refresh token.
253+
254+ Returns:
255+ Tuple[str, str, Optional[datetime], Mapping[str, str]]: The access token,
256+ refresh token, expiration, and additional data returned by the token
257+ endpoint. If response_data doesn't have refresh token, then the current
258+ refresh token will be returned.
259+
260+ Raises:
261+ google.auth.exceptions.RefreshError: If the token endpoint returned
262+ an error.
263+ """
264+ try :
265+ access_token = response_data ["access_token" ]
266+ except KeyError as caught_exc :
267+ new_exc = exceptions .RefreshError ("No access token in response." , response_data )
268+ six .raise_from (new_exc , caught_exc )
269+
270+ refresh_token = response_data .get ("refresh_token" , refresh_token )
271+ expiry = _parse_expiry (response_data )
272+
273+ return access_token , refresh_token , expiry , response_data
274+
275+
207276def refresh_grant (
208- request , token_uri , refresh_token , client_id , client_secret , scopes = None
277+ request ,
278+ token_uri ,
279+ refresh_token ,
280+ client_id ,
281+ client_secret ,
282+ scopes = None ,
283+ rapt_token = None ,
209284):
210285 """Implements the OAuth 2.0 refresh token grant.
211286
@@ -224,10 +299,11 @@ def refresh_grant(
224299 scopes must be authorized for the refresh token. Useful if refresh
225300 token has a wild card scope (e.g.
226301 'https://www.googleapis.com/auth/any-api').
302+ rapt_token (Optional(str)): The reauth Proof Token.
227303
228304 Returns:
229- Tuple[str, Optional[ str] , Optional[datetime], Mapping[str, str]]: The
230- access token, new refresh token, expiration, and additional data
305+ Tuple[str, str, Optional[datetime], Mapping[str, str]]: The access
306+ token, new or current refresh token, expiration, and additional data
231307 returned by the token endpoint.
232308
233309 Raises:
@@ -244,16 +320,8 @@ def refresh_grant(
244320 }
245321 if scopes :
246322 body ["scope" ] = " " .join (scopes )
323+ if rapt_token :
324+ body ["rapt" ] = rapt_token
247325
248326 response_data = _token_endpoint_request (request , token_uri , body )
249-
250- try :
251- access_token = response_data ["access_token" ]
252- except KeyError as caught_exc :
253- new_exc = exceptions .RefreshError ("No access token in response." , response_data )
254- six .raise_from (new_exc , caught_exc )
255-
256- refresh_token = response_data .get ("refresh_token" , refresh_token )
257- expiry = _parse_expiry (response_data )
258-
259- return access_token , refresh_token , expiry , response_data
327+ return _handle_refresh_grant_response (response_data , refresh_token )
0 commit comments