Skip to content

Commit 31e988d

Browse files
committed
feat: expose refresh token in iam authenticator
1 parent 05563e7 commit 31e988d

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

ibm_cloud_sdk_core/iam_token_manager.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from typing import Dict, Optional
1818
from .jwt_token_manager import JWTTokenManager
1919

20+
# pylint: disable=too-many-instance-attributes
2021
class IAMTokenManager(JWTTokenManager):
2122
"""The IAMTokenManager takes an api key and performs the necessary interactions with
2223
the IAM token service to obtain and store a suitable bearer token. Additionally, the IAMTokenManager
@@ -61,6 +62,7 @@ class IAMTokenManager(JWTTokenManager):
6162
"""
6263
DEFAULT_IAM_URL = 'https://iam.cloud.ibm.com/identity/token'
6364
CONTENT_TYPE = 'application/x-www-form-urlencoded'
65+
OPERATION_PATH = "/identity/token"
6466
REQUEST_TOKEN_GRANT_TYPE = 'urn:ibm:params:oauth:grant-type:apikey'
6567
REQUEST_TOKEN_RESPONSE_TYPE = 'cloud_iam'
6668
TOKEN_NAME = 'access_token'
@@ -77,10 +79,15 @@ def __init__(self,
7779
proxies: Optional[Dict[str, str]] = None,
7880
scope: Optional[str] = None) -> None:
7981
self.apikey = apikey
80-
self.url = url if url else self.DEFAULT_IAM_URL
82+
self.url = url
83+
if url == "":
84+
self.url = self.DEFAULT_IAM_URL
85+
elif url.endswith(self.OPERATION_PATH):
86+
self.url = url[:-len(self.OPERATION_PATH)]
8187
self.client_id = client_id
8288
self.client_secret = client_secret
8389
self.headers = headers
90+
self.refresh_token = None
8491
self.proxies = proxies
8592
self.scope = scope
8693
super().__init__(
@@ -118,7 +125,7 @@ def request_token(self) -> dict:
118125

119126
response = self._request(
120127
method='POST',
121-
url=self.url,
128+
url=self.url + self.OPERATION_PATH,
122129
headers=headers,
123130
data=data,
124131
auth_tuple=auth_tuple,
@@ -146,6 +153,11 @@ def set_headers(self, headers: Dict[str, str]) -> None:
146153
else:
147154
raise TypeError('headers must be a dictionary')
148155

156+
def _save_token_info(self, token_response: dict) -> None:
157+
super()._save_token_info(token_response)
158+
159+
self.refresh_token = token_response.get("refresh_token")
160+
149161
def set_proxies(self, proxies: Dict[str, str]) -> None:
150162
"""Sets the proxies the token manager will use to communicate with IAM on behalf of the host.
151163

test/test_iam_token_manager.py

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,89 @@ def test_request_token_auth_in_setter_scope():
256256
assert responses.calls[0].request.headers.get('Authorization') is None
257257
assert responses.calls[0].response.text == response
258258
assert 'scope=john+snow' in responses.calls[0].response.request.body
259+
260+
@responses.activate
261+
def test_get_refresh_token():
262+
iam_url = "https://iam.cloud.ibm.com/identity/token"
263+
access_token_str = get_access_token()
264+
response = """{
265+
"access_token": "%s",
266+
"token_type": "Bearer",
267+
"expires_in": 3600,
268+
"expiration": 1524167011,
269+
"refresh_token": "jy4gl91BQ"
270+
}""" % (access_token_str)
271+
responses.add(responses.POST, url=iam_url, body=response, status=200)
272+
273+
token_manager = IAMTokenManager("iam_apikey")
274+
token_manager.get_token()
275+
276+
assert len(responses.calls) == 2
277+
assert token_manager.refresh_token == "jy4gl91BQ"
278+
279+
# In order to test with a live IAM server, create file "iamtest.env" in the project root.
280+
# It should look like this:
281+
282+
# IAMTEST1_AUTH_URL=<url> e.g. https://iam.cloud.ibm.com
283+
# IAMTEST1_AUTH_TYPE=iam
284+
# IAMTEST1_APIKEY=<apikey>
285+
286+
# IAMTEST2_AUTH_URL=<url> e.g. https://iam.test.cloud.ibm.com/identity/token
287+
# IAMTEST2_AUTH_TYPE=iam
288+
# IAMTEST2_APIKEY=<apikey>
289+
# IAMTEST2_CLIENT_ID=<client id>
290+
# IAMTEST2_CLIENT_SECRET=<client secret>
291+
292+
# Then uncomment the function below and run this command:
293+
# python3 -m pytest test -k "test_iam_live_token_server"
294+
295+
# import os
296+
# from ibm_cloud_sdk_core import get_authenticator_from_environment
297+
298+
# def test_iam_live_token_server():
299+
# # Get two iam authenticators from the environment.
300+
# # "iamtest1" uses username/password
301+
# # "iamtest2" uses username/apikey
302+
# os.environ['IBM_CREDENTIALS_FILE'] = "iamtest.env"
303+
304+
305+
# # Test "iamtest1" service
306+
# auth1 = get_authenticator_from_environment("iamtest1")
307+
# assert auth1 is not None
308+
# assert auth1.token_manager is not None
309+
# assert auth1.token_manager.url is not None
310+
311+
# request = {'method': "GET"}
312+
# request["url"] = ""
313+
# request["headers"] = {}
314+
315+
# assert auth1.token_manager.refresh_token is None
316+
317+
# auth1.authenticate(request)
318+
319+
# assert auth1.token_manager.refresh_token is not None
320+
321+
# assert request.get("headers") is not None
322+
# assert request["headers"].get("Authorization") is not None
323+
# assert "Bearer " in request["headers"].get("Authorization")
324+
325+
326+
# # Test "iamtest2" service
327+
# auth2 = get_authenticator_from_environment("iamtest2")
328+
# assert auth2 is not None
329+
# assert auth2.token_manager is not None
330+
# assert auth2.token_manager.url is not None
331+
332+
# request = {'method': "GET"}
333+
# request["url"] = ""
334+
# request["headers"] = {}
335+
336+
# assert auth2.token_manager.refresh_token is None
337+
338+
# auth2.authenticate(request)
339+
340+
# assert auth2.token_manager.refresh_token is not None
341+
342+
# assert request.get("headers") is not None
343+
# assert request["headers"].get("Authorization") is not None
344+
# assert "Bearer " in request["headers"].get("Authorization")

0 commit comments

Comments
 (0)