Unpin JWT and use integer IAT values

PyJWT 2.6.0 began performing validation of iat (issued at) claims
in 9cb9401cc5

I believe the intent of RFC7519 is to support any numeric values
(including floating point) for iat, nbf, and exp, however, the
PyJWT library has made the assumption that the values should be
integers, and therefore when we supply an iat with decimal seconds,
PyJWT will round down when validating the value. In our unit tests,
this can cause validation errors.

In order to avoid any issues, we will round down the times that
we supply when generating JWT tokens and supply them as integers
in accordance with the robustness principle.

Change-Id: Ia8341b4d5de827e2df8878f11f2d1f52a1243cd4
This commit is contained in:
James E. Blair 2022-11-15 13:52:53 -08:00
parent 4d555ca675
commit 3780ed548c
6 changed files with 49 additions and 49 deletions

View File

@ -129,8 +129,8 @@ For example, in Python, and for an authenticator using the ``HS256`` algorithm:
>>> jwt.encode({'sub': 'user1',
'iss': <issuer_id>,
'aud': <client_id>,
'iat': time.time(),
'exp': time.time() + 300,
'iat': int(time.time()),
'exp': int(time.time()) + 300,
'zuul': {
'admin': ['tenant-one']
}

View File

@ -23,7 +23,7 @@ alembic
cryptography>=1.6
cachecontrol<0.12.7
cachetools
pyjwt>=2.0.0,<2.6.0
pyjwt>=2.0.0
iso8601
psutil
fb-re2>=1.0.6

View File

@ -92,7 +92,7 @@ class TestOpenIDConnectAuthenticator(BaseTestCase):
payload = {
'iss': FAKE_WELL_KNOWN_CONFIG['issuer'],
'aud': config['client_id'],
'exp': time.time() + 3600,
'exp': int(time.time()) + 3600,
'sub': 'someone'
}
token = jwt.encode(

View File

@ -2063,7 +2063,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='OnlyZuulNoDana',
algorithm='HS256')
resp = self.post_url(
@ -2125,7 +2125,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() - 3600}
'exp': int(time.time()) - 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.post_url(
@ -2160,7 +2160,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-six', 'tenant-ten', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.post_url(
@ -2194,7 +2194,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'aud': 'zuul.example.com',
'sub': 'testuser',
'zuul': {'admin': ['tenant-one', ]},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
args = {"reason": "some reason",
"count": 1,
'job': 'project-test2',
@ -2234,7 +2234,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -2286,7 +2286,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
request_id, _ = self._init_autohold_delete(authz)
# now try the autohold-delete API
bad_authz = {'iss': 'zuul_operator',
@ -2295,7 +2295,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-two', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
bad_token = jwt.encode(bad_authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.delete_url(
@ -2313,7 +2313,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
bad_token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.delete_url(
@ -2328,7 +2328,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
request_id, token = self._init_autohold_delete(authz)
resp = self.delete_url(
"api/tenant/tenant-one/autohold/%s" % request_id,
@ -2352,7 +2352,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
path = "api/tenant/%(tenant)s/project/%(project)s/enqueue"
@ -2404,7 +2404,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(path % enqueue_args,
@ -2448,7 +2448,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
path = "api/tenant/%(tenant)s/project/%(project)s/dequeue"
@ -2565,8 +2565,8 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600,
'iat': time.time()}
'exp': int(time.time()) + 3600,
'iat': int(time.time())}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -2654,8 +2654,8 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600,
'iat': time.time()}
'exp': int(time.time()) + 3600,
'iat': int(time.time())}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -2746,8 +2746,8 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600,
'iat': time.time()}
'exp': int(time.time()) + 3600,
'iat': int(time.time())}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -2795,7 +2795,7 @@ class TestTenantScopedWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one'],
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.get_url(
@ -2838,7 +2838,7 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -2877,21 +2877,21 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
authz = {'iss': 'zuul_operator',
'aud': 'zuul.example.com',
'sub': 'venkman',
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
_test_project_enqueue_with_authz(i, p, authz, 200)
i += 1
# Unauthorized sub
authz = {'iss': 'zuul_operator',
'aud': 'zuul.example.com',
'sub': 'vigo',
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
_test_project_enqueue_with_authz(i, p, authz, 403)
i += 1
# unauthorized issuer
authz = {'iss': 'columbia.edu',
'aud': 'zuul.example.com',
'sub': 'stantz',
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
_test_project_enqueue_with_authz(i, p, authz, 401)
self.waitUntilSettled()
@ -2905,7 +2905,7 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
'aud': 'zuul.example.com',
'sub': 'melnitz',
'groups': ['ghostbusters', 'secretary'],
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
path = "api/tenant/%(tenant)s/project/%(project)s/enqueue"
@ -2931,7 +2931,7 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
'sub': 'zeddemore',
'vehicle': {
'car': 'ecto-1'},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
path = "api/tenant/%(tenant)s/project/%(project)s/enqueue"
@ -2953,7 +2953,7 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
'aud': 'zuul.example.com',
'sub': 'testuser',
'zuul': {'admin': admin_tenants},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.get_url('/api/tenant/tenant-one/authorizations',
@ -2991,7 +2991,7 @@ class TestTenantScopedWebApiWithAuthRules(BaseTestWeb):
for test_user in users:
authz = test_user['authz']
authz['exp'] = time.time() + 3600
authz['exp'] = int(time.time()) + 3600
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.get_url('/api/tenant/tenant-one/authorizations',
@ -3031,7 +3031,7 @@ class TestTenantScopedWebApiTokenWithExpiry(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.post_url(
@ -3066,8 +3066,8 @@ class TestTenantScopedWebApiTokenWithExpiry(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 7200,
'iat': time.time() + 3600}
'exp': int(time.time()) + 7200,
'iat': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
resp = self.post_url(
@ -3102,8 +3102,8 @@ class TestTenantScopedWebApiTokenWithExpiry(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600,
'iat': time.time()}
'exp': int(time.time()) + 3600,
'iat': int(time.time())}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
time.sleep(10)
@ -3146,8 +3146,8 @@ class TestTenantScopedWebApiTokenWithExpiry(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ],
},
'exp': time.time() + 3600,
'iat': time.time()}
'exp': int(time.time()) + 3600,
'iat': int(time.time())}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
req = self.post_url(
@ -3249,7 +3249,7 @@ class TestCLIViaWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -3288,7 +3288,7 @@ class TestCLIViaWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -3317,7 +3317,7 @@ class TestCLIViaWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -3356,7 +3356,7 @@ class TestCLIViaWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -3407,7 +3407,7 @@ class TestCLIViaWebApi(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(

View File

@ -187,7 +187,7 @@ class TestZuulClientAdmin(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -227,7 +227,7 @@ class TestZuulClientAdmin(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -263,7 +263,7 @@ class TestZuulClientAdmin(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -308,7 +308,7 @@ class TestZuulClientAdmin(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(
@ -359,7 +359,7 @@ class TestZuulClientAdmin(BaseTestWeb):
'zuul': {
'admin': ['tenant-one', ]
},
'exp': time.time() + 3600}
'exp': int(time.time()) + 3600}
token = jwt.encode(authz, key='NoDanaOnlyZuul',
algorithm='HS256')
p = subprocess.Popen(

View File

@ -735,7 +735,7 @@ class Client(zuul.cmd.ZuulApp):
print('"%s" authenticator configuration not found.'
% self.args.auth_config)
sys.exit(1)
now = time.time()
now = int(time.time())
token = {'iat': now,
'exp': now + self.args.expires_in,
'iss': get_default(self.config, auth_section, 'issuer_id'),