### Full FastAPI Keycloak Middleware Example Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md A comprehensive example demonstrating the setup of FastAPI with Keycloak middleware, including custom claim extraction and verification configuration. Ensure the `verify` attribute is correctly set for certificate validation. ```python from fastapi import FastAPI from fastapi_keycloak_middleware import KeycloakConfiguration, setup_keycloak_middleware # Set up Keycloak connection keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", claims=["sub", "name", "email", "your-claim"], # Modify claims reject_on_missing_claim=False, # Control behaviour when claims are missing verify="/ca.pem" # Can be True, False or the path to the CA file used to sign certs ) async def map_user(userinfo: typing.Dict[str, typing.Any]) -> User: """ Map userinfo extracted from the claim to something you can use in your application. You could - Verify user presence in your database - Create user if it doesn't exist (depending on your application architecture) """ user = make_sure_user_exists(userinfo) # Replace with your logic return user def get_user(request: Request, db: Session = Depends(get_db)): """ Custom dependency to retrieve the user object from the request. """ if "user" in request.scope: # Do whatever you need to get the user object from the database user = User.get_by_id(db, request.scope["user"].id) if user: return user # Handle missing user scenario raise HTTPException( status_code=401, detail="Unable to retrieve user from request", ) app = FastAPI() ``` -------------------------------- ### Install fastapi-keycloak-middleware with Poetry Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/intro.md Use this command to add the package to your project if you are using Poetry for dependency management. ```bash poetry add fastapi-keycloak-middleware ``` -------------------------------- ### Basic FastAPI Keycloak Middleware Setup Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Integrate the Keycloak middleware into your FastAPI application with basic configuration. This setup handles token parsing, validation, user information extraction, and OpenAPI schema updates. ```python from fastapi import FastAPI from fastapi_keycloak_middleware import KeycloakConfiguration, setup_keycloak_middleware # Set up Keycloak keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", ) app = FastAPI() # Add middleware with basic config setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, ) @app.get("/") async def root(): return {"message": "Hello World"} ``` -------------------------------- ### Install fastapi-keycloak-middleware with Pip Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/intro.md Use this command to add the package to your project if you are using pip for dependency management. ```bash pip install fastapi-keycloak-middleware ``` -------------------------------- ### Setup Keycloak Middleware Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Configure and add the Keycloak middleware to your FastAPI application. Ensure you have a valid Keycloak configuration and a user mapping function. ```python setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, user_mapper=map_user, ) @app.get("/") async def root(user: User = Depends(get_user)): return {"message": "Hello World"} ``` -------------------------------- ### FastAPI Middleware Order Example (Correct) Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Illustrates the correct middleware order for FastAPI, ensuring CORS middleware is added after Keycloak authentication middleware to properly handle preflight requests. ```python from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from fastapi_keycloak_middleware import setup_keycloak_middleware, KeycloakConfiguration keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", ) app = FastAPI() # Add Keycloak middleware first setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, ) # Add CORS middleware last (so it runs first for requests) app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) ``` -------------------------------- ### Configure OR Match Strategy for Permissions Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Modify the `CheckPermissions` behavior to require only one of the specified permissions by setting `match_strategy=MatchStrategy.OR`. This example shows the configuration within the `CheckPermissions` dependency. ```python from fastapi import Depends from fastapi_keycloak_middleware import CheckPermissions, MatchStrategy # Assume 'app' is defined elsewhere # app = FastAPI() @app.get("/view_user", dependencies=[Depends(CheckPermissions(["user:view", "user:view_own"], match_strategy=MatchStrategy.OR))]) def view_user(): return {"userinfo": "Hello World"} ``` -------------------------------- ### FastAPI Middleware Order Example (Incorrect) Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Demonstrates an incorrect middleware order where CORS middleware is added after Keycloak authentication middleware, leading to issues with OPTIONS preflight requests. ```python from fastapi import FastAPI from starlette.middleware.cors import CORSMiddleware from fastapi_keycloak_middleware import setup_keycloak_middleware, KeycloakConfiguration keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", ) app = FastAPI() # Adding CORS after auth first - THIS CAUSES ISSUES! app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # Add Keycloak middleware second setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, ) ``` -------------------------------- ### Example User Info Claims Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md This JSON object illustrates the typical claims structure expected from a Keycloak access token when using the default configuration. These claims are available in the `userinfo` parameter of the custom user mapper. ```json { "sub": "1234567890", "name": "John Doe", "family_name": "Doe", "given_name": "John", "preferred_username": "jdoe", "email": "jon.doe@example.com" } ``` -------------------------------- ### Configure Claim for Authorization Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Customize the claim used for extracting scopes during authorization. By default, 'roles' is used; this example shows how to use a 'permissions' claim instead. ```python keycloak_config = KeycloakConfiguration( # ... authorization_method=AuthorizationMethod.CLAIM, authorization_claim="permissions" ) setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, get_user=auth_get_user, ) ``` -------------------------------- ### Testing with Specific User and Roles Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md Perform a GET request to an endpoint, specifying a custom user and additional roles via request headers. This test case validates authorization logic with explicitly defined user credentials and permissions. ```python # Set user and roles on a per request basis def test_with_specific_user_and_roles_on_request(app): client = TestClient(app) response = client.get("/api/v1/your-endpoint", headers={"X-User": "test_user_2", "X-Roles": "Admin"}) ``` -------------------------------- ### Testing with Default User Roles Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md Execute a GET request to a specified endpoint using the default user and roles configured in the mocked `get_auth` dependency. This test verifies the application's behavior with standard authentication. ```python # Use default user1 with User role, do not specify anything def test_with_default_user(app): client = TestClient(app) response = client.get("/api/v1/your-endpoint") ``` -------------------------------- ### Testing with Default User Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md This test uses the mocked `app` fixture and makes a GET request to '/api/v1/your-endpoint' without specifying a user, thus using the default user ('test_user_1') defined in the `mocked_get_user` function. ```python # Use default user1, do not specify anything def test_with_default_user(app): client = TestClient(app) response = client.get("/api/v1/your-endpoint") ``` -------------------------------- ### Mocking Keycloak Authentication Middleware Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md This fixture mocks the Keycloak middleware setup and overrides the `get_user` dependency to use a custom mocked version. This allows for testing without actual authentication. Ensure you import your application's `get_user` dependency or the middleware's default one. ```python from your-library import get_user # or from fastapi_keycloak_middleware import get_user async def mocked_get_user(request: Request): """ This function can be used as FastAPI dependency to easily retrieve the user object from DB """ user = request.headers.get("X-User", "test_user_1") # Use whatever method you typically to fetch the user object return crud.user.get_by_username(sessionmanager.get_session(), user) @pytest.fixture(scope="session") def app(session_mocker): # Mock auth middleware, effectively remove it session_mocker.patch("fastapi_keycloak_middleware.setup_keycloak_middleware") # Its important to import the app after the middleware has been mocked from backend.main import app as backend_app # Override the get_user dependency with our mock method above backend_app.dependency_overrides[get_user] = mocked_get_user yield backend_app ``` -------------------------------- ### Custom User Retrieval Dependency Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Implement a custom dependency function to retrieve the user object, especially when only a unique identifier is stored in the request scope to avoid ORM lazy loading issues. This example shows fetching the user from a database using a session. ```python def get_user(request: Request, db: Session = Depends(get_db)): """ Custom dependency to retrieve the user object from the request. """ if "user" in request.scope: # Do whatever you need to get the user object from the database user = User.get_by_id(db, request.scope["user"].id) if user: return user # Handle missing user scenario raise HTTPException( status_code=401, detail="Unable to retrieve user from request", ) @app.get("/") async def root(user: User = Depends(get_user)): return {"message": "Hello World"} ``` -------------------------------- ### Access Authorization Result as a Dependency Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Retrieve the `AuthorizationResult` as a FastAPI dependency within a path operation function. This allows you to inspect which permissions actually matched and react accordingly, for example, to check if the requested resource belongs to the logged-in user. ```python from fastapi import Depends from fastapi_keycloak_middleware import AuthorizationResult, CheckPermissions, MatchStrategy # Assume 'app' is defined elsewhere # app = FastAPI() @app.get("/view_user") def view_user(authorization_result: AuthorizationResult = Depends(CheckPermissions(["user:view", "user:view_own"], match_strategy=MatchStrategy.OR))): return {"userinfo": "Hello World"} ``` -------------------------------- ### Enable Authorization Middleware Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Configure and set up the Keycloak middleware for authorization. Ensure Keycloak details and the authorization method are correctly specified. ```python from fastapi import FastAPI from fastapi_keycloak_middleware import KeycloakConfiguration, AuthorizationMethod, setup_keycloak_middleware # Set up Keycloak keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", authorization_method=AuthorizationMethod.CLAIM, ) app = FastAPI() setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, get_user=auth_get_user, ) ``` -------------------------------- ### Format and lint code with Ruff Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/README.md Run these commands to ensure your code adheres to the project's formatting and linting standards using Ruff. ```bash ruff check . ruff format . ``` -------------------------------- ### Configure Keycloak with Custom Claims Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Set up Keycloak configuration to extract additional claims from the token. Use this when you need specific user information beyond the standard claims. ```python # Set up Keycloak keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", claims=["sub", "name", "email", "your-claim"], # Modify claims reject_on_missing_claim=False, # Control behaviour when claims are missing ) ``` -------------------------------- ### Testing with Specific User (Per Request) Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md This test demonstrates how to specify a user for an individual request by passing the 'X-User' header directly in the `client.get` call. This overrides any default headers set on the client. ```python # Set user on a per request basis def test_with_specific_user_on_request(app): client = TestClient(app) response = client.get("/api/v1/your-endpoint", headers={"X-User": "test_user_2"}) ``` -------------------------------- ### Enable Swagger UI Integration with Keycloak Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Configure Keycloak middleware to integrate with Swagger UI for authentication testing. This requires a separate public Keycloak client and setting `add_swagger_auth` to True. ```python keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", swagger_client_id="", swagger_auth_scopes=["openid", "profile"], # Optional swagger_auth_pkce=True, # Optional swagger_scheme_name="keycloak" # Optional ) setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, add_swagger_auth=True ) ``` -------------------------------- ### Configure Device Authentication Claim Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Enable and configure device authentication by specifying a claim in the access token that identifies a device. This bypasses normal user authentication if the claim is present and truthy. ```python keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", enable_device_authentication=True, device_authentication_claim="is_device", ) ``` -------------------------------- ### Testing with Specific User (All Requests) Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md This test configures the `TestClient` with a default header 'X-User' set to 'your-other-user'. All requests made by this client instance will use this specified user. ```python # Use specific user for all requests in this test def test_with_specific_user(app: FastAPI): client = TestClient(app, headers={"X-User": "your-other-user"}) response = client.get("/api/v1/your-endpoint") ``` -------------------------------- ### Mounting Multiple FastAPI Applications Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Mount different FastAPI applications to a main application to enforce distinct authentication policies on various endpoints. Ensure middleware is set up on the secured app. ```python secured_app = FastAPI() setup_keycloak_middleware( secured_app, # ... exclude_patterns=excluded_routes, ) public_app = FastAPI() app = FastAPI() app.mount(path="/secured", app=secured_app) app.mount(path="/public", app=public_app) ``` -------------------------------- ### Mocking get_auth Dependency for Testing Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/testing.md Override the `get_auth` dependency to return specific roles based on request headers for testing authorization logic. This mock allows you to simulate different user permissions by setting `X-User` and `X-Roles` headers. ```python async def mocked_get_auth(request: Request): user = request.headers.get("X-User", "test_user_1") # Set default roles based on the user if user.startswith("test_user_admin"): roles = ["Admin", "User"] elif user.startswith("test_user_guest"): roles = ["Guest"] elif user.startswith("test_user_"): roles = ["User"] else: roles = [user] # Add additional roles if requested requested_roles = request.headers.get("X-Roles", "") if requested_roles: roles += requested_roles.split(",") # Optionally, if you normally use an auth_mapper, you can apply it now # roles = auth_mapper(roles) # or if using async: # roles = await auth_mapper(roles) return roles @pytest.fixture(scope="session") def app(session_mocker): # Mock auth middleware, effectively remove it session_mocker.patch("fastapi_keycloak_middleware.setup_keycloak_middleware") # Its important to import the app after the middleware has been mocked from backend.main import app as backend_app # Override the get_user dependency with our mock method above backend_app.dependency_overrides[get_user] = mocked_get_user backend_app.dependency_overrides[get_auth] = mocked_get_auth yield backend_app ``` -------------------------------- ### Customize User Getter with Custom Mapper Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Provide a custom asynchronous function to the `user_mapper` parameter during middleware initialization to transform Keycloak user info into a custom `User` object. The `userinfo` dictionary contains claims from the access token. ```python async def map_user(userinfo: typing.Dict[str, typing.Any]) -> User: # Do something with the userinfo return User() # Add middleware with basic config setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, user_mapper=map_user, ) ``` -------------------------------- ### Enforce Multiple Permissions with CheckPermissions Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Use the `CheckPermissions` dependency to enforce that a user possesses all specified permissions. This is applied using the `dependencies` parameter in FastAPI route definitions. ```python from fastapi import Depends from fastapi_keycloak_middleware import CheckPermissions # Assume 'app' is defined elsewhere # app = FastAPI() @app.get("/view_user", dependencies=[Depends(CheckPermissions(["user:view", "user:view_own"]))]) def view_user(): return {"userinfo": "Hello World"} ``` -------------------------------- ### Configure JWT Decoding Options Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Modify JWT verification options, such as claim checking, by passing a dictionary to `validation_options`. Refer to JWCrypto documentation for available options. ```python # Set up Keycloak keycloak_config = KeycloakConfiguration( # ... validation_options={ "check_claims": { "jti": None, "exp": None, "iat": None, } } ) ``` -------------------------------- ### Protect Endpoint with Permission Check Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Use the `CheckPermissions` dependency to protect a FastAPI endpoint. Specify the required permission string within the dependency. ```python from fastapi import Depends from fastapi_keycloak_middleware import CheckPermissions @app.get("/protected", dependencies=[Depends(CheckPermissions("protected"))]) def protected(): return {"message": "Hello World"} ``` -------------------------------- ### Custom Scope Mapper for Role to Permission Mapping Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/authorization.md Implement a custom scope mapper to translate token roles into internal permissions. This function is executed for every request, so keep its logic efficient or consider externalizing complex lookups. ```python from fastapi import FastAPI from fastapi_keycloak_middleware import KeycloakConfiguration, AuthorizationMethod, setup_keycloak_middleware import typing # Assume 'rules' and 'log' are defined elsewhere # rules = {...} # log = ... async def scope_mapper(claim_auth: typing.List[str]) -> typing.List[str]: """ Map token roles to internal permissions. This could be whatever code you like it to be, you could also fetch this from database. Keep in mind this is done for every incoming request though. """ permissions = [] for role in claim_auth: try: permissions += rules[role] except KeyError: log.warning("Unknown role %s" % role) return permissions keycloak_config = KeycloakConfiguration( # ... authorization_method=AuthorizationMethod.CLAIM, ) # Assume 'app' and 'auth_get_user' are defined elsewhere # app = FastAPI() # auth_get_user = ... setup_keycloak_middleware( app, keycloak_configuration=keycloak_config, get_user=auth_get_user, scope_mapper=scope_mapper, ) ``` -------------------------------- ### Retrieve User with Default Dependency Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Use the `get_user` dependency provided by the middleware to easily retrieve the authenticated user object in your FastAPI endpoints. This assumes the user object has been stored in the request scope. ```python from fastapi_keycloak_middleware import get_user @app.get("/") async def root(user: User = Depends(get_user)): return {"message": "Hello World"} ``` -------------------------------- ### Exclude Specific Paths from Authentication Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Configure the middleware to bypass authentication for a list of specified paths. These paths are compiled into regular expressions and matched against the request path. ```python excluded_routes = [ "/status", "/docs", "/openapi.json", "/redoc", ] app = FastAPI() setup_keycloak_middleware( app, # ... exclude_patterns=excluded_routes, ) ``` -------------------------------- ### Configure Keycloak with Token Introspection Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Use this configuration to validate tokens via the introspection endpoint instead of local JWT signature verification. This requires an additional request to Keycloak for each token validation. ```python # Set up Keycloak keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", use_introspection_endpoint=True ) ``` -------------------------------- ### Modifying Function Signature to Include Request Parameter Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Demonstrates the 'hacky' method used by the decorator to modify a function's signature to include the `request` parameter, allowing access to request details without user declaration. This involves inspecting and replacing the function signature. ```python # Get function signature sig = signature(func) # Get parameters parameters: OrderedDict = sig.parameters if "request" in parameters.keys(): # Request is already present, no need to modify signature return wrapper # Add request parameter by creating a new parameter list based on the old one parameters = [ Parameter( name="request", kind=Parameter.POSITIONAL_OR_KEYWORD, default=Parameter.empty, annotation=starlette.requests.Request, ), *parameters.values(), ] # Create a new signature, as the signature is immutable new_sig = sig.replace(parameters=parameters, return_annotation=sig.return_annotation) # Update the wrapper function signature wrapper.__signature__ = new_sig return wrapper ``` -------------------------------- ### Change Authentication Scheme Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/usage.md Modify the default 'Bearer' authentication scheme to 'Token' by setting the `authentication_scheme` in `KeycloakConfiguration`. This allows the middleware to accept custom authorization header prefixes. ```python keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", authentication_scheme="Token" ) ``` -------------------------------- ### Decorator-based Permission Checking with Request Injection Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Illustrates how the `@require_permission` decorator injects the `Request` object into a path function, even if not explicitly declared by the user. This is a deprecated feature. ```python from fastapi import Request from middleware import require_permission @app.get("/users/me") @require_permission("user:read") def read_users_me(request: Request): # pylint: disable=unused-argument return {"user": "Hello World"} ``` -------------------------------- ### Disable Token Validation in Keycloak Middleware Source: https://github.com/waza-ari/fastapi-keycloak-middleware/blob/main/docs/advanced_topics.md Disables all token validation, which is a significant security risk and bypasses the middleware's security features. Use with extreme caution. ```python # Set up Keycloak keycloak_config = KeycloakConfiguration( url="https://sso.your-keycloak.com", realm="", client_id="", client_secret="", validate_token=False ) ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.