def test_that_user_must_have_all_permissions(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa can_view = security.user_permission("users:view") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list, can_view))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) bad_token = make_access_token(sub="test-user", permissions=["users:list"]) valid_token = make_access_token( sub="JaneDoe", permissions=["users:list", "users:view"], ) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {bad_token}"}) assert resp.status_code == 403 assert resp.json() == {"detail": "Missing required permission users:view"} resp = client.get("/users", headers={"Authorization": f"Bearer {valid_token}"}) assert resp.status_code == 200 (user1,) = resp.json() assert user1["auth"]["subject"] == "JaneDoe"
def test_that_nonexisting_permissions_are_ignored(app, client): security = FastAPISecurity() @app.get("/users/me") def get_user_info(user: User = Depends(security.authenticated_user_or_401)): return user.without_access_token() security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token( sub="test-subject", permissions=["users:list"], ) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get( "/users/me", headers={"Authorization": f"Bearer {access_token}"} ) assert resp.status_code == 200 data = resp.json()["auth"] del data["expires_at"] del data["issued_at"] assert data == { "audience": ["https://some-resource"], "auth_method": "oauth2", "issuer": "https://identity-provider/", "permissions": [], "scopes": [], "subject": "test-subject", }
def test_that_header_is_returned_for_oauth2(app, client): security = FastAPISecurity() security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/") assert resp.headers["WWW-Authenticate"] == "Bearer"
def test_that_headers_are_returned_for_oauth2_and_basic_auth(app, client): security = FastAPISecurity() security.init_basic_auth([{"username": "******", "password": "******"}]) security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/") # NOTE: They are actually set as separate headers assert resp.headers["WWW-Authenticate"] == "Basic, Bearer"
def test_that_uninitialized_basic_auth_doesnt_accept_any_credentials( app, client): security = FastAPISecurity() @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] # NOTE: Not passing basic_auth_credentials, which means Basic Auth will be disabled # NOTE: We are passing security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) resp = client.get("/") assert resp.status_code == 401 resp = client.get("/", auth=("username", "password")) assert resp.status_code == 401
def test_that_oauth2_rejects_expired_token(app, client): security = FastAPISecurity() @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token(sub="test-subject", expire_in=-1) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/", headers={"Authorization": f"Bearer {access_token}"}) assert resp.status_code == 401
def test_that_oauth2_rejects_incorrect_token(app, client): security = FastAPISecurity() @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) resp = client.get("/") assert resp.status_code == 401 resp = client.get("/", headers={"Authorization": "Bearer abc"}) assert resp.status_code == 401 resp = client.get("/", headers={"Authorization": "Bearer abc.xyz.def"}) assert resp.status_code == 401
def test_that_missing_permission_results_in_403(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token(sub="test-user", permissions=[]) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {access_token}"}) assert resp.status_code == 403 assert resp.json() == {"detail": "Missing required permission users:list"}
def test_that_assigned_permission_result_in_200(app, client): security = FastAPISecurity() can_list = security.user_permission("users:list") # noqa @app.get("/users") def get_user_list(user: User = Depends(security.user_holding(can_list))): return [user] security.init_oauth2_through_jwks(dummy_jwks_uri, audiences=[dummy_audience]) access_token = make_access_token(sub="test-user", permissions=["users:list"]) with aioresponses() as mock: mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) resp = client.get("/users", headers={"Authorization": f"Bearer {access_token}"}) assert resp.status_code == 200 (user1,) = resp.json() assert user1["auth"]["subject"] == "test-user"
settings = get_settings() security = FastAPISecurity() if settings.basic_auth_credentials: security.init_basic_auth(settings.basic_auth_credentials) if settings.oidc_discovery_url: security.init_oauth2_through_oidc( settings.oidc_discovery_url, audiences=settings.oauth2_audiences, ) elif settings.oauth2_jwks_url: security.init_oauth2_through_jwks( settings.oauth2_jwks_url, audiences=settings.oauth2_audiences, ) security.add_permission_overrides(settings.permission_overrides or {}) logger = logging.getLogger(__name__) create_product_perm = security.user_permission("products:create") @app.get("/users/me") async def get_user_details(user: User = Depends(security.user_with_info)): """Return user details, regardless of whether user is authenticated or not""" return user.without_access_token()