async def test_malformed_token_handling(): http_auth_with_malformed_token = HTTPAuthorizationCredentials( scheme="a", credentials="malformed-token", ) verifier = JWKsVerifier(jwks=JWKS.null()) with pytest.raises(HTTPException): await verifier._get_publickey(http_auth_with_malformed_token) with pytest.raises(HTTPException): await verifier.verify_token(http_auth_with_malformed_token) verifier = JWKsVerifier(jwks=JWKS.null(), auto_error=False) assert not await verifier._get_publickey(http_auth_with_malformed_token) assert not await verifier.verify_token(http_auth_with_malformed_token) verifier = ScopedJWKsVerifier(jwks=JWKS.null()) with pytest.raises(HTTPException): verifier._verify_scope(http_auth_with_malformed_token) with pytest.raises(HTTPException): await verifier.verify_token(http_auth_with_malformed_token) verifier = ScopedJWKsVerifier(jwks=JWKS.null(), auto_error=False) assert not verifier._verify_scope(http_auth_with_malformed_token) assert not await verifier.verify_token(http_auth_with_malformed_token)
def test_verify_scope_exeption(mocker): mocker.patch( "fastapi_cloudauth.verification.jwt.get_unverified_claims", return_value={"dummy key": "read:test"}, ) scope_key = "dummy key" http_auth = HTTPAuthorizationCredentials( scheme="a", credentials="dummy-token", ) # trivial scope verifier = ScopedJWKsVerifier(jwks=JWKS.null(), scope_key=scope_key, scope_name=None) assert verifier._verify_scope(http_auth) # invalid incoming scope format mocker.patch( "fastapi_cloudauth.verification.jwt.get_unverified_claims", return_value={"dummy key": 100}, ) verifier = ScopedJWKsVerifier(jwks=JWKS.null(), scope_key=scope_key, scope_name=["read:test"]) with pytest.raises(HTTPException): verifier._verify_scope(http_auth) # auto_error is False verifier = ScopedJWKsVerifier( jwks=JWKS.null(), scope_key=scope_key, scope_name=["read:test"], auto_error=False, ) assert not verifier._verify_scope(http_auth)
async def test_jwks_test_mode(): # instantiate null jwks obj (no querying jwks) _jwks = JWKS.null() # instantiate fixed jwks obj (no querying jwks) dummy = Key(None, None) _jwks = JWKS(fixed_keys={"test": dummy}) assert await _jwks.get_publickey("test") == dummy
async def test_assign_user_info(auth): """three way to set user info schema 1. pass it to arguments when create instance 2. call `.claim` method and pass it to that arguments 3. assign with `=` statements """ class SubSchema(BaseModel): sub: str class NameSchema(BaseModel): name: str class IatSchema(BaseModel): iat: int # authorized token dummy_http_auth = HTTPAuthorizationCredentials( scheme="a", credentials= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Im5hbWUiLCJpYXQiOjE1MTYyMzkwMjJ9.3ZEDmhWNZWDbJDPDlZX_I3oaalNYXdoT-bKLxIxQK4U", ) user = auth(jwks=JWKS.null(), user_info=IatSchema) assert await user.call(dummy_http_auth) == IatSchema(iat=1516239022) assert await user.claim(SubSchema).call(dummy_http_auth) == SubSchema( sub="1234567890") user.user_info = NameSchema assert await user.call(dummy_http_auth) == NameSchema(name="name")
async def test_extract_raw_user_info(auth): dummy_http_auth = HTTPAuthorizationCredentials( scheme="a", credentials= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Im5hbWUiLCJpYXQiOjE1MTYyMzkwMjJ9.3ZEDmhWNZWDbJDPDlZX_I3oaalNYXdoT-bKLxIxQK4U", ) class NameSchema(BaseModel): name: str get_current_user = auth(jwks=JWKS.null(), user_info=NameSchema) get_current_user.user_info = None res = await get_current_user.call(dummy_http_auth) assert res == {"sub": "1234567890", "name": "name", "iat": 1516239022} get_current_user = auth(jwks=JWKS.null(), user_info=NameSchema) res = await get_current_user.claim(None).call(dummy_http_auth) assert res == {"sub": "1234567890", "name": "name", "iat": 1516239022}
async def test_forget_def_user_info(auth): dummy_http_auth = HTTPAuthorizationCredentials( scheme="a", credentials= "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Im5hbWUiLCJpYXQiOjE1MTYyMzkwMjJ9.3ZEDmhWNZWDbJDPDlZX_I3oaalNYXdoT-bKLxIxQK4U", ) """If `.user_info` is None, return raw payload""" get_current_user = auth(jwks=JWKS.null()) assert get_current_user.user_info is None res = await get_current_user.call(dummy_http_auth) assert res == {"sub": "1234567890", "name": "name", "iat": 1516239022}
def test_scope_match_all(mocker, scopes): scope_key = "dummy key" http_auth = HTTPAuthorizationCredentials( scheme="a", credentials="dummy-token", ) # check scope logic mocker.patch( "fastapi_cloudauth.verification.jwt.get_unverified_claims", return_value={"dummy key": scopes}, ) jwks = JWKS.null() # api scope < user scope verifier = ScopedJWKsVerifier( scope_name=["xxx:xxx"], jwks=jwks, scope_key=scope_key, auto_error=False, ) assert verifier._verify_scope(http_auth) # api scope == user scope (in order) verifier = ScopedJWKsVerifier( scope_name=["xxx:xxx", "yyy:yyy"], jwks=jwks, scope_key=scope_key, auto_error=False, ) assert verifier._verify_scope(http_auth) # api scope == user scope (disorder) verifier = ScopedJWKsVerifier( scope_name=["yyy:yyy", "xxx:xxx"], jwks=jwks, scope_key=scope_key, auto_error=False, ) assert verifier._verify_scope(http_auth) # api scope > user scope verifier = ScopedJWKsVerifier( scope_name=["yyy:yyy", "xxx:xxx", "zzz:zzz"], jwks=jwks, scope_key=scope_key, auto_error=False, ) assert not verifier._verify_scope(http_auth)
def test_return_instance_with_scope(): # scope method return new instance to give it for Depends. verifier = ScopedAuth(jwks=JWKS(keys=[])) # must set scope_key (Inherit ScopedAuth and override scope_key attribute) scope_key = "dummy key" verifier.scope_key = scope_key scope_name = "required-scope" obj = verifier.scope(scope_name) assert isinstance(obj, ScopedAuth) assert obj.scope_key == scope_key, "scope_key mustn't be cleared." assert obj.scope_name == scope_name, "Must set scope_name in returned instanse." assert (obj.verifier._jwks_to_key == verifier.verifier._jwks_to_key ), "return cloned objects" assert (obj.verifier.auto_error == verifier.verifier.auto_error ), "return cloned objects"
def test_validation_scope(mocker, scopes): mocker.patch( "fastapi_cloudauth.verification.jwt.get_unverified_claims", return_value={"dummy key": scopes}, ) verifier = ScopedAuth(jwks=JWKS.null()) scope_key = "dummy key" verifier.scope_key = scope_key scope_name = "user-assigned-scope" obj = verifier.scope(scope_name) assert obj.verifier._verify_scope( HTTPAuthorizationCredentials(scheme="", credentials="")) scope_name = "user-assigned-scope-invalid" obj = verifier.scope(scope_name) with pytest.raises(HTTPException): obj.verifier._verify_scope( HTTPAuthorizationCredentials(scheme="", credentials="")) obj.verifier.auto_error = False assert not obj.verifier._verify_scope( HTTPAuthorizationCredentials(scheme="", credentials=""))
def test_raise_error_invalid_set_scope(): # scope_key is not declaired token_verifier = ScopedAuth(jwks=JWKS.null()) with pytest.raises(AttributeError): # raise AttributeError for invalid instanse attributes wrt scope token_verifier.scope("read:test")
def test_verify_token(): verifier = JWKsVerifier(jwks=JWKS.null()) verifier_no_error = JWKsVerifier(jwks=JWKS.null(), auto_error=False) # correct token = jwt.encode( { "sub": "dummy-ID", "exp": datetime.utcnow() + timedelta(hours=10), "iat": datetime.utcnow(), }, "dummy_secret", headers={ "alg": "HS256", "typ": "JWT", "kid": "dummy-kid" }, ) verifier._verify_claims( HTTPAuthorizationCredentials(scheme="a", credentials=token)) verifier_no_error._verify_claims( HTTPAuthorizationCredentials(scheme="a", credentials=token)) # token expired token = jwt.encode( { "sub": "dummy-ID", "exp": datetime.utcnow() - timedelta(hours=10), # 10h before "iat": datetime.utcnow(), }, "dummy_secret", headers={ "alg": "HS256", "typ": "JWT", "kid": "dummy-kid" }, ) e = _assert_verifier(token, verifier) assert e.status_code == HTTP_401_UNAUTHORIZED and e.detail == messages.NOT_VERIFIED _assert_verifier_no_error(token, verifier_no_error) # token created at future token = jwt.encode( { "sub": "dummy-ID", "exp": datetime.utcnow() + timedelta(hours=10), "iat": datetime.utcnow() + timedelta(hours=10), }, "dummy_secret", headers={ "alg": "HS256", "typ": "JWT", "kid": "dummy-kid" }, ) e = _assert_verifier(token, verifier) assert e.status_code == HTTP_401_UNAUTHORIZED and e.detail == messages.NOT_VERIFIED _assert_verifier_no_error(token, verifier_no_error) # invalid format token = jwt.encode( { "sub": "dummy-ID", "exp": datetime.utcnow() + timedelta(hours=10), "iat": datetime.utcnow(), }, "dummy_secret", headers={ "alg": "HS256", "typ": "JWT", "kid": "dummy-kid" }, ) token = token.split(".")[0] e = _assert_verifier(token, verifier) assert (e.status_code == HTTP_401_UNAUTHORIZED and e.detail == messages.NOT_AUTHENTICATED) _assert_verifier_no_error(token, verifier_no_error)