1# Copyright 2021 Google LLC 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13"""Tests for tink.python.tink.jwt._jwt_validator.""" 14 15import datetime 16 17from absl.testing import absltest 18from tink import jwt 19from tink.jwt import _jwt_validator 20 21 22class JwtValidatorTest(absltest.TestCase): 23 24 def test_validator_getters(self): 25 fixed_now = datetime.datetime.fromtimestamp(12345, datetime.timezone.utc) 26 clock_skew = datetime.timedelta(minutes=1) 27 validator = jwt.new_validator( 28 expected_type_header='type_header', 29 expected_issuer='issuer', 30 expected_audience='audience', 31 fixed_now=fixed_now, 32 clock_skew=clock_skew) 33 self.assertTrue(validator.has_expected_type_header()) 34 self.assertEqual(validator.expected_type_header(), 'type_header') 35 self.assertTrue(validator.has_expected_issuer()) 36 self.assertEqual(validator.expected_issuer(), 'issuer') 37 self.assertTrue(validator.has_expected_audience()) 38 self.assertEqual(validator.expected_audience(), 'audience') 39 self.assertFalse(validator.allow_missing_expiration()) 40 self.assertFalse(validator.ignore_issuer()) 41 self.assertFalse(validator.ignore_audiences()) 42 self.assertTrue(validator.has_fixed_now()) 43 self.assertEqual(validator.fixed_now(), fixed_now) 44 self.assertEqual(validator.clock_skew(), clock_skew) 45 46 def test_validator_ignore_getters(self): 47 validator = jwt.new_validator( 48 allow_missing_expiration=True, 49 ignore_type_header=True, 50 ignore_issuer=True, 51 ignore_audiences=True) 52 self.assertTrue(validator.allow_missing_expiration()) 53 self.assertTrue(validator.ignore_type_header()) 54 self.assertTrue(validator.ignore_issuer()) 55 self.assertTrue(validator.ignore_audiences()) 56 57 def test_empty_validator_getters(self): 58 validator = jwt.new_validator() 59 self.assertFalse(validator.has_expected_type_header()) 60 self.assertFalse(validator.has_expected_issuer()) 61 self.assertFalse(validator.has_expected_audience()) 62 self.assertFalse(validator.has_fixed_now()) 63 self.assertFalse(validator.clock_skew(), datetime.timedelta()) 64 65 def test_too_much_clock_skew(self): 66 with self.assertRaises(ValueError): 67 jwt.new_validator(clock_skew=datetime.timedelta(minutes=20)) 68 69 def test_validate_expired_fails(self): 70 expired = (datetime.datetime.now(tz=datetime.timezone.utc) 71 - datetime.timedelta(minutes=1)) 72 token = jwt.new_raw_jwt(expiration=expired) 73 validator = jwt.new_validator() 74 with self.assertRaises(jwt.JwtInvalidError): 75 _jwt_validator.validate(validator, token) 76 77 def test_validate_not_expired_success(self): 78 still_valid = (datetime.datetime.now(tz=datetime.timezone.utc) 79 + datetime.timedelta(minutes=1)) 80 token = jwt.new_raw_jwt(expiration=still_valid) 81 validator = jwt.new_validator() 82 _jwt_validator.validate(validator, token) 83 84 def test_validate_token_that_expires_now_fails(self): 85 now = datetime.datetime.fromtimestamp(1234.0, tz=datetime.timezone.utc) 86 token = jwt.new_raw_jwt(expiration=now) 87 validator = jwt.new_validator() 88 with self.assertRaises(jwt.JwtInvalidError): 89 _jwt_validator.validate(validator, token) 90 91 def test_validate_recently_expired_with_clock_skew_success(self): 92 recently_expired = (datetime.datetime.now(tz=datetime.timezone.utc) 93 - datetime.timedelta(minutes=1)) 94 token = jwt.new_raw_jwt(expiration=recently_expired) 95 validator = jwt.new_validator(clock_skew=datetime.timedelta(minutes=2)) 96 # because of clock_skew, the recently expired token is valid 97 _jwt_validator.validate(validator, token) 98 99 def test_validate_not_before_in_the_future_fails(self): 100 in_the_future = (datetime.datetime.now(tz=datetime.timezone.utc) 101 + datetime.timedelta(minutes=1)) 102 token = jwt.new_raw_jwt(not_before=in_the_future, without_expiration=True) 103 validator = jwt.new_validator(allow_missing_expiration=True) 104 with self.assertRaises(jwt.JwtInvalidError): 105 _jwt_validator.validate(validator, token) 106 107 def test_validate_not_before_in_the_past_success(self): 108 in_the_past = (datetime.datetime.now(tz=datetime.timezone.utc) 109 - datetime.timedelta(minutes=1)) 110 token = jwt.new_raw_jwt(not_before=in_the_past, without_expiration=True) 111 validator = jwt.new_validator(allow_missing_expiration=True) 112 _jwt_validator.validate(validator, token) 113 114 def test_validate_not_before_is_now_success(self): 115 now = datetime.datetime.fromtimestamp(12345, datetime.timezone.utc) 116 token = jwt.new_raw_jwt(not_before=now, without_expiration=True) 117 validator = jwt.new_validator(allow_missing_expiration=True) 118 _jwt_validator.validate(validator, token) 119 120 def test_validate_not_before_almost_reached_with_clock_skew_success(self): 121 in_one_minute = (datetime.datetime.now(tz=datetime.timezone.utc) 122 + datetime.timedelta(minutes=1)) 123 token = jwt.new_raw_jwt(not_before=in_one_minute, without_expiration=True) 124 validator = jwt.new_validator( 125 allow_missing_expiration=True, clock_skew=datetime.timedelta(minutes=2)) 126 _jwt_validator.validate(validator, token) 127 128 def test_validate_issued_at(self): 129 in_one_minute = (datetime.datetime.now(tz=datetime.timezone.utc) 130 + datetime.timedelta(minutes=1)) 131 one_minute_ago = (datetime.datetime.now(tz=datetime.timezone.utc) 132 - datetime.timedelta(minutes=1)) 133 token_with_issued_at_in_the_future = jwt.new_raw_jwt( 134 issued_at=in_one_minute, without_expiration=True) 135 token_with_issued_at_in_the_past = jwt.new_raw_jwt( 136 issued_at=one_minute_ago, without_expiration=True) 137 token_without_issued_at = jwt.new_raw_jwt(without_expiration=True) 138 139 validator = jwt.new_validator(allow_missing_expiration=True) 140 _jwt_validator.validate(validator, token_with_issued_at_in_the_future) 141 _jwt_validator.validate(validator, token_with_issued_at_in_the_past) 142 _jwt_validator.validate(validator, token_without_issued_at) 143 144 issued_at_validator = jwt.new_validator( 145 expect_issued_in_the_past=True, allow_missing_expiration=True) 146 with self.assertRaises(jwt.JwtInvalidError): 147 _jwt_validator.validate(issued_at_validator, 148 token_with_issued_at_in_the_future) 149 _jwt_validator.validate(issued_at_validator, 150 token_with_issued_at_in_the_past) 151 with self.assertRaises(jwt.JwtInvalidError): 152 _jwt_validator.validate(issued_at_validator, token_without_issued_at) 153 154 def test_validate_issued_at_with_clock_skew(self): 155 in_one_minute = (datetime.datetime.now(tz=datetime.timezone.utc) 156 + datetime.timedelta(minutes=1)) 157 token_one_minute_in_the_future = jwt.new_raw_jwt( 158 issued_at=in_one_minute, without_expiration=True) 159 160 validator_without_clock_skew = jwt.new_validator( 161 expect_issued_in_the_past=True, allow_missing_expiration=True) 162 with self.assertRaises(jwt.JwtInvalidError): 163 _jwt_validator.validate(validator_without_clock_skew, 164 token_one_minute_in_the_future) 165 166 validator_with_clock_skew = jwt.new_validator( 167 expect_issued_in_the_past=True, 168 allow_missing_expiration=True, 169 clock_skew=datetime.timedelta(minutes=2)) 170 _jwt_validator.validate(validator_with_clock_skew, 171 token_one_minute_in_the_future) 172 173 def test_requires_type_header_but_no_type_header_set_fails(self): 174 token = jwt.new_raw_jwt(without_expiration=True) 175 validator = jwt.new_validator( 176 expected_type_header='type_header', allow_missing_expiration=True) 177 with self.assertRaises(jwt.JwtInvalidError): 178 _jwt_validator.validate(validator, token) 179 180 def test_invalid_type_header_fails(self): 181 token = jwt.new_raw_jwt(type_header='unknown', without_expiration=True) 182 validator = jwt.new_validator( 183 expected_type_header='type_header', allow_missing_expiration=True) 184 with self.assertRaises(jwt.JwtInvalidError): 185 _jwt_validator.validate(validator, token) 186 187 def test_correct_type_header_success(self): 188 token = jwt.new_raw_jwt(type_header='type_header', without_expiration=True) 189 validator = jwt.new_validator( 190 expected_type_header='type_header', allow_missing_expiration=True) 191 _jwt_validator.validate(validator, token) 192 193 def test_type_header_in_token_but_not_in_validator_fails(self): 194 validator = jwt.new_validator(allow_missing_expiration=True) 195 token_with_type_header = jwt.new_raw_jwt( 196 type_header='type_header', without_expiration=True) 197 with self.assertRaises(jwt.JwtInvalidError): 198 _jwt_validator.validate(validator, token_with_type_header) 199 200 def test_ignore_type_header_success(self): 201 validator = jwt.new_validator( 202 ignore_type_header=True, allow_missing_expiration=True) 203 token_without_type_header = jwt.new_raw_jwt(without_expiration=True) 204 _jwt_validator.validate(validator, token_without_type_header) 205 token_with_type_header = jwt.new_raw_jwt( 206 type_header='type_header', without_expiration=True) 207 _jwt_validator.validate(validator, token_with_type_header) 208 209 def test_requires_issuer_but_no_issuer_set_fails(self): 210 token = jwt.new_raw_jwt(without_expiration=True) 211 validator = jwt.new_validator( 212 expected_issuer='issuer', allow_missing_expiration=True) 213 with self.assertRaises(jwt.JwtInvalidError): 214 _jwt_validator.validate(validator, token) 215 216 def test_invalid_issuer_fails(self): 217 token = jwt.new_raw_jwt(issuer='unknown', without_expiration=True) 218 validator = jwt.new_validator( 219 expected_issuer='issuer', allow_missing_expiration=True) 220 with self.assertRaises(jwt.JwtInvalidError): 221 _jwt_validator.validate(validator, token) 222 223 def test_correct_issuer_success(self): 224 token = jwt.new_raw_jwt(issuer='issuer', without_expiration=True) 225 validator = jwt.new_validator( 226 expected_issuer='issuer', allow_missing_expiration=True) 227 _jwt_validator.validate(validator, token) 228 229 def test_issuer_in_token_but_not_in_validator_fails(self): 230 validator = jwt.new_validator(allow_missing_expiration=True) 231 token_with_issuer = jwt.new_raw_jwt( 232 issuer='issuer', without_expiration=True) 233 with self.assertRaises(jwt.JwtInvalidError): 234 _jwt_validator.validate(validator, token_with_issuer) 235 236 def test_ignore_issuer_success(self): 237 validator = jwt.new_validator( 238 ignore_issuer=True, allow_missing_expiration=True) 239 token_without_issuer = jwt.new_raw_jwt(without_expiration=True) 240 _jwt_validator.validate(validator, token_without_issuer) 241 token_with_issuer = jwt.new_raw_jwt( 242 issuer='issuer', without_expiration=True) 243 _jwt_validator.validate(validator, token_with_issuer) 244 245 def test_requires_audience_but_no_audience_set_fails(self): 246 token = jwt.new_raw_jwt(without_expiration=True) 247 validator = jwt.new_validator( 248 expected_audience='audience', allow_missing_expiration=True) 249 with self.assertRaises(jwt.JwtInvalidError): 250 _jwt_validator.validate(validator, token) 251 252 def test_wrong_audience_fails(self): 253 token = jwt.new_raw_jwt(audiences=['unknown'], without_expiration=True) 254 validator = jwt.new_validator( 255 expected_audience='audience', allow_missing_expiration=True) 256 with self.assertRaises(jwt.JwtInvalidError): 257 _jwt_validator.validate(validator, token) 258 259 def test_correct_audience_success(self): 260 token = jwt.new_raw_jwt(audiences=['you', 'me'], without_expiration=True) 261 validator = jwt.new_validator( 262 expected_audience='me', allow_missing_expiration=True) 263 _jwt_validator.validate(validator, token) 264 265 def test_audience_in_token_but_not_in_validator_fails(self): 266 validator = jwt.new_validator(allow_missing_expiration=True) 267 token_with_audience = jwt.new_raw_jwt( 268 audiences=['audience'], without_expiration=True) 269 with self.assertRaises(jwt.JwtInvalidError): 270 _jwt_validator.validate(validator, token_with_audience) 271 272 def test_no_audience_success(self): 273 validator = jwt.new_validator(allow_missing_expiration=True) 274 token = jwt.new_raw_jwt(without_expiration=True) 275 _jwt_validator.validate(validator, token) 276 277 def test_ignore_audiences_success(self): 278 validator = jwt.new_validator( 279 ignore_audiences=True, allow_missing_expiration=True) 280 token_without_audience = jwt.new_raw_jwt(without_expiration=True) 281 _jwt_validator.validate(validator, token_without_audience) 282 token_with_audience = jwt.new_raw_jwt( 283 audiences=['audience'], without_expiration=True) 284 _jwt_validator.validate(validator, token_with_audience) 285 286 def test_validate_with_fixed_now_expired_fails(self): 287 in_two_minutes = ( 288 datetime.datetime.now(tz=datetime.timezone.utc) + 289 datetime.timedelta(minutes=2)) 290 in_one_minute = in_two_minutes - datetime.timedelta(minutes=1) 291 token = jwt.new_raw_jwt(expiration=in_one_minute) 292 validator = jwt.new_validator(fixed_now=in_two_minutes) 293 with self.assertRaises(jwt.JwtInvalidError): 294 _jwt_validator.validate(validator, token) 295 296 def test_validate_with_fixed_now_not_yet_valid_fails(self): 297 two_minutes_ago = ( 298 datetime.datetime.now(tz=datetime.timezone.utc) - 299 datetime.timedelta(minutes=2)) 300 one_minute_ago = two_minutes_ago + datetime.timedelta(minutes=1) 301 token = jwt.new_raw_jwt(not_before=one_minute_ago, without_expiration=True) 302 validator = jwt.new_validator(fixed_now=two_minutes_ago) 303 with self.assertRaises(jwt.JwtInvalidError): 304 _jwt_validator.validate(validator, token) 305 306 def test_validate_with_fixed_now_valid_success(self): 307 fixed_now = datetime.datetime.fromtimestamp(12345, datetime.timezone.utc) 308 validator = jwt.new_validator(fixed_now=fixed_now) 309 expiration = fixed_now + datetime.timedelta(minutes=1) 310 not_before = fixed_now - datetime.timedelta(minutes=1) 311 token = jwt.new_raw_jwt(expiration=expiration, not_before=not_before) 312 _jwt_validator.validate(validator, token) 313 314 def test_invalid_clock_skew_fail(self): 315 with self.assertRaises(ValueError): 316 jwt.new_validator(clock_skew=datetime.timedelta(minutes=1000)) 317 318 def test_fixed_now_without_timezone_fail(self): 319 with self.assertRaises(ValueError): 320 jwt.new_validator(fixed_now=datetime.datetime.fromtimestamp(12345)) 321 322 323if __name__ == '__main__': 324 absltest.main() 325