Skip to content

Commit 2d213c5

Browse files
authored
Merge pull request #128 from britive/develop
v1.6.1rc5
2 parents 8e4c900 + 7c4febe commit 2d213c5

4 files changed

Lines changed: 62 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@
22

33
* As of v1.4.0 release candidates will be published in an effort to get new features out faster while still allowing time for full QA testing before moving the release candidate to a full release.
44

5+
## v1.6.1rc5 [2023-12-15]
6+
#### What's New
7+
* None
8+
9+
#### Enhancements
10+
* None
11+
12+
#### Bug Fixes
13+
* Switch to extracting expiration time from the JWT instead of calculating based on auth time + session duration
14+
15+
#### Dependencies
16+
* None
17+
18+
#### Other
19+
* Additional debug logging related to the authentication process
20+
521
## v1.6.1rc4 [2023-12-14]
622
#### What's New
723
* None

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[metadata]
22
name = pybritive
3-
version = 1.6.1rc4
3+
version = 1.6.1rc5
44
author = Britive Inc.
55
author_email = support@britive.com
66
description = A pure Python CLI for Britive

src/pybritive/britive_cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ def login(self, explicit: bool = False, browser: str = None):
129129
except exceptions.UnauthorizedRequest as e:
130130
if '401 - e0000' in str(e).lower():
131131
self.print(f'attempt {counter} of 3 - login failed')
132+
self.debug(f'login error message was {str(e)}')
132133
self.logout()
133134
else:
134135
raise e

src/pybritive/helpers/credentials.py

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@
3030
]
3131

3232

33-
# the credentials should expire sooner than the true expiration date
34-
# in case we need to do things like polling for credentials during
35-
# an approval process
36-
credential_expiration_safe_zone_minutes = 0
3733
federation_provider_default_expiration_seconds = 900
3834

3935

@@ -45,6 +41,10 @@ def b64_encode_url_safe(value: bytes):
4541
return base64.urlsafe_b64encode(value).decode('utf-8').replace('=', '')
4642

4743

44+
class CouldNotExtractExpirationTimeFromJwtException(Exception):
45+
pass
46+
47+
4848
# this base class expects self.credentials to be a dict - so sub classes need to convert to dict
4949
class CredentialManager:
5050
def __init__(self, tenant_name: str, tenant_alias: str, cli: any, federation_provider: str = None,
@@ -119,11 +119,23 @@ def perform_interactive_login(self):
119119
else:
120120
credentials = response.json()['authenticationResult']
121121

122-
# calculate a safe expiration time
123-
auth_time = int(credentials.get('authTime', 0))
124-
session_time = int(credentials.get('maxSessionTimeout', 0))
125-
creds_expire_after = auth_time + session_time - (credential_expiration_safe_zone_minutes * 60 * 1000)
126-
credentials['safeExpirationTime'] = creds_expire_after
122+
try:
123+
# attempt to pull the expiration time from the jwt
124+
expiration_time_ms = self._extract_exp_from_jwt(
125+
token=credentials['accessToken'],
126+
verify=False,
127+
convert_to_ms=True
128+
)
129+
self.cli.debug(f'found expiration time {expiration_time_ms} from jwt')
130+
except CouldNotExtractExpirationTimeFromJwtException:
131+
# calculate from other fields in the authentication result
132+
self.cli.debug('could not extract token expiration time from jwt - dropping to use other fields')
133+
auth_time = int(credentials.get('authTime', 0))
134+
session_time = int(credentials.get('maxSessionTimeout', 0))
135+
expiration_time_ms = auth_time + session_time
136+
self.cli.debug(f'found expiration time {expiration_time_ms} from authTime + maxSessionTimeout')
137+
138+
credentials['safeExpirationTime'] = expiration_time_ms
127139

128140
# drop a bunch of unnecessary fields
129141
for field in interactive_login_fields_to_pop:
@@ -133,6 +145,24 @@ def perform_interactive_login(self):
133145
self.cli.print(f'Authenticated to tenant {self.tenant} via interactive login.')
134146
break
135147

148+
@staticmethod
149+
def _extract_exp_from_jwt(token: str, verify: bool = False, convert_to_ms: bool = False):
150+
try:
151+
expiration_time = jwt.decode(
152+
token,
153+
# validation of the token will occur on the Britive backend
154+
# so not verifying everything here is okay since we are just
155+
# trying to extract the token expiration time so we can store
156+
# it in the ~/.britive/pybritive.credentials[.encrypted] file
157+
options={
158+
'verify_signature': verify,
159+
'verify_aud': verify
160+
}
161+
)['exp']
162+
return expiration_time * (1000 if convert_to_ms else 1)
163+
except Exception:
164+
raise CouldNotExtractExpirationTimeFromJwtException
165+
136166
def perform_federation_provider_authentication(self):
137167
self.cli.print(f'Performing {self.federation_provider} federation provider authentication '
138168
f'against tenant {self.tenant}.')
@@ -168,17 +198,11 @@ def perform_federation_provider_authentication(self):
168198
token_expires = json.loads(token)['iam_request_headers']['x-britive-expires']
169199
expiration_time = int(parser.parse(token_expires).timestamp() * 1000)
170200
if provider == 'oidc':
171-
expiration_time = jwt.decode(
172-
token,
173-
# validation of the token will occur on the Britive backend
174-
# so not verifying everything here is okay since we are just
175-
# trying to extract the token expiration time so we can store
176-
# it in the ~/.britive/pybritive.credentials[.encrypted] file
177-
options={
178-
'verify_signature': False,
179-
'verify_aud': False
180-
}
181-
)['exp'] * 1000
201+
expiration_time = self._extract_exp_from_jwt(
202+
token=token,
203+
verify=False,
204+
convert_to_ms=True
205+
)
182206
except Exception:
183207
self.cli.print(f'Cannot obtain token expiration time for {self.federation_provider}. Defaulting to '
184208
f'{federation_provider_default_expiration_seconds} seconds.')

0 commit comments

Comments
 (0)