def test_that_header_is_returned_for_basic_auth(app, client): security = FastAPISecurity() security.init_basic_auth([{"username": "******", "password": "******"}]) @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] resp = client.get("/") assert resp.headers["WWW-Authenticate"] == "Basic"
def test_that_basic_auth_accepts_correct_credentials(app, client): security = FastAPISecurity() @app.get("/") def get_products(user: User = Depends(security.authenticated_user_or_401)): return [] credentials = [{"username": "******", "password": "******"}] security.init_basic_auth(credentials) resp = client.get("/", auth=("user", "pass")) assert resp.status_code == 200
def test_that_user_with_info_dependency_works_unauthenticated(app, client): security = FastAPISecurity() @app.get("/users/me") def get_user_info(user: User = Depends(security.user_with_info)): return user.without_access_token() security.init_basic_auth([{"username": "******", "password": "******"}]) resp = client.get("/users/me") assert resp.status_code == 200 info = resp.json()["info"] assert info["nickname"] is None
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_explicit_permission_overrides_are_applied(app, client): cred = HTTPBasicCredentials(username="******", password="******") security = FastAPISecurity() create_product_perm = security.user_permission("products:create") security.init_basic_auth([cred]) security.add_permission_overrides({"johndoe": ["products:create"]}) @app.post("/products") def create_product( user: User = Depends(security.user_holding(create_product_perm)), ): return {"ok": True} resp = client.post("/products", auth=("johndoe", "123")) assert resp.status_code == 200 assert resp.json() == {"ok": True}
def test_that_user_dependency_works_authenticated_or_not(app, client): security = FastAPISecurity() @app.get("/users/me") def get_user_info(user: User = Depends(security.user)): return user.without_access_token() security.init_basic_auth([{"username": "******", "password": "******"}]) # Anonymous resp = client.get("/users/me") assert resp.status_code == 200 data = resp.json()["auth"] del data["expires_at"] del data["issued_at"] assert data == { "audience": [], "auth_method": "none", "issuer": None, "permissions": [], "scopes": [], "subject": "anonymous", } # Authenticated resp = client.get("/users/me", auth=("JaneDoe", "abc123")) assert resp.status_code == 200 data = resp.json()["auth"] del data["expires_at"] del data["issued_at"] assert data == { "audience": [], "auth_method": "basic_auth", "issuer": None, "permissions": [], "scopes": [], "subject": "JaneDoe", }
def test_that_authenticated_user_with_info_or_401_works_as_expected(app, client): security = FastAPISecurity() @app.get("/users/me") def get_user_info( user: User = Depends(security.authenticated_user_with_info_or_401), ): return user.without_access_token() security.init_oauth2_through_oidc(dummy_oidc_url, audiences=[dummy_audience]) security.init_basic_auth([{"username": "******", "password": "******"}]) with aioresponses() as mock: mock.get( dummy_oidc_url, payload={ "userinfo_endpoint": dummy_userinfo_endpoint_url, "jwks_uri": dummy_jwks_uri, }, ) mock.get(dummy_jwks_uri, payload=dummy_jwks_response_data) mock.get(dummy_userinfo_endpoint_url, payload={"nickname": "jacobsvante"}) token = make_access_token(sub="GMqBbybGfBQeR6NgCY4NyXKnpFzaaTAn@clients") resp = client.get("/users/me", headers={"Authorization": f"Bearer {token}"}) assert resp.status_code == 200 info = resp.json()["info"] assert info["nickname"] == "jacobsvante" # Basic auth resp = client.get("/users/me", auth=("a", "b")) assert resp.status_code == 200 info = resp.json()["info"] assert info["nickname"] is None # Unauthenticated resp = client.get("/users/me") assert resp.status_code == 401 assert resp.json() == {"detail": "Could not validate credentials"}
def test_that_permission_overrides_can_be_an_exhaustable_iterator(app, client): cred = HTTPBasicCredentials(username="******", password="******") security = FastAPISecurity() create_product_perm = security.user_permission("products:create") security.init_basic_auth([cred]) overrides = iter(["products:create"]) security.add_permission_overrides({"johndoe": overrides}) @app.post("/products") def create_product( user: User = Depends(security.user_holding(create_product_perm)), ): return {"ok": True} # NOTE: Before v0.3.1, the second iteration would give a HTTP403, as the overrides # iterator had been exhausted on the first try. for _ in range(2): resp = client.post("/products", auth=("johndoe", "123")) assert resp.status_code == 200 assert resp.json() == {"ok": True}
from fastapi import Depends, FastAPI from fastapi_security import FastAPISecurity, User from . import db from .models import Product from .settings import get_settings app = FastAPI() 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__)