Skip to content
Matthias Osswald edited this page May 6, 2021 · 7 revisions

Fastapi & Open Policy Agent

Table of Contents

Getting Started with FastAPI app with Authentication and Authorization

This setup includes a local setup incl. a minimal FastAPI app with Authentication using Keycloak and Authorization using Open Policy Agent.

Setup the App

Create a new FastAPI app in app.py.

Install these packages:

  • uvicorn to serve the app.
  • fastapi the web framework.
  • fastapi-opa the extension as a authentication/authorization middleware.
# app.py

from typing import Dict

from fastapi import FastAPI

from fastapi_opa import OPAConfig
from fastapi_opa import OPAMiddleware
from fastapi_opa.auth import OIDCAuthentication
from fastapi_opa.auth import OIDCConfig

opa_host = "http://localhost:8181"
oidc_config = OIDCConfig(
    well_known_endpoint="http://localhost:8080/auth/realms/example-realm/.well-known/openid-configuration",  # noqa
    app_uri="http://localhost:4000",
    client_id="example-client",
    client_secret="bbb4857c-21ba-44a3-8843-1364984a36906",
)
oidc_auth = OIDCAuthentication(oidc_config)
opa_config = OPAConfig(authentication=oidc_auth, opa_host=opa_host)

app = FastAPI()
app.add_middleware(OPAMiddleware, config=opa_config)

@app.get("/finance/salary/{name}")
async def salary(name: str) -> Dict:
    return {"msg": "success", "name": name}

The opa_host refers to the OPA instance we are going to set up. It will be used to send a query (JSON string) which is evaluated by OPAs policies.

The oidc_config is used to connect to Keycloak. To authenticate the user with Keycloak as a broker it is necessary to configure Keycloak first since a trust bond has to be established between the two services.

You should be able to run the app now on port 4000 using:

uvicorn app:app --port 4000

If you now visit localhost:4000 you should be redirected to the id-broker configured on port 8080 (which is not set up yet).

Setup Keycloak & OPA

For this local setup, docker-compose is used. The Keycloak admin user can be configured using the environment variables KEYCLOAK_USER and KEYCLOAK_PASSWORD. Keycloak is configured to run on port 8080.

OPA is loading the auth.rego policy in the policy directory on start. The OPA instance is configured to run on port 8181.

# docker-compose.yml

version: '2'
services:
  keycloak:
    image: jboss/keycloak:latest
    ports:
      - 8080:8080
    environment:
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=admin
  opa:
    image: openpolicyagent/opa:latest
    ports:
      - 8181:8181
    command:
      - "run"
      - "--server"
      - "--log-level=debug"
      - "/policy/auth.rego"
    volumes:
      - ./policy:/policy

Let's add a simple policy now (mostly equals to this OPA guide):

# policy/auth.rego

package httpapi.authz

# bob is alice's manager, and betty is charlie's.
subordinates = {"alice": [], "charlie": [], "bob": ["alice"], "betty": ["charlie"]}

# HTTP API request
import input

default allow = false

# Allow users to get their own salaries.
allow {
  some username
  input.request_method == "GET"
  input.request_path = ["finance", "salary", username]
  input.user == username
}

# Allow managers to get their subordinates' salaries.
allow {
  some username
  input.request_method == "GET"
  input.request_path = ["finance", "salary", username]
  subordinates[input.user][_] == username
}

Your project should now look like this:

.
├── main.py
├── docker-compose.yml
└── policy
    └── auth.rego

OPA and Keycloak can now be started with:

docker-compose up [-d]

You can now visit the admin panel from Keycloak (http://localhost:8080/auth/) and OPA provides you with a playground (http://localhost:8181/).

Configure Keycloak

To get started Keycloak has to be configured and some users are needed for the test environment.

Create new realm

On the top left (saying master) you can add a new realm. Let´s call it example-realm (app configs realm in the OIDC config of the app).

Create new client

Let´s call the client example-client (app configs client_id) and select openid-connect as a Client Protocol.

Now change/add these fields:

And then save the changes.

Retrieve client id and secret

In the confidential tab of that client you can now choose Client Authenticator Client Id and Secret and generate a secret. Add the secret as your app's client_secret.

By now you should be able successfully redirect your client to the login screen of Keycloak calling http://localhost:4000/finance/salary/alice.

Create users

Create the users alice, betty, bob, charlie and david now and add attributes:

For Alice:

Key Value
azp alice
hr false
subordinates []
user alice

For Betty:

Key Value
azp betty
hr false
subordinates ["charlie"]
user betty

For Bob

Key Value
azp bob
hr false
subordinates ["alice"]
user bob

For Charlie

Key Value
azp charlie
hr false
subordinates []
user charlie

For David

Key Value
azp David
hr false
subordinates []
user david

Create mappings

The mappings for the attributes can be created in your client settings. Add a mapper with the same names as the attribute key for each attribute to send it with the token.

Create passwords for the users

In each users settings go to the Credentials tab and create a password for them (disable Temporary).

Test the Setup

If you now call http://localhost:4000/finance/salary/alice again you are redirected to Keycloak. There you can login with your password set for Alice.

If the login was successful you will be redirected back to the app where you should see a success message and your name (alice).

Under the hood the client verified your JSON web token after the redirect to the app. The web tokens information then was sent to OPA to check if you are permitted to see the information.

You can now play with this setup with e.g. these tests:

  • Call the same endpoint (/finance/salary/alice) but login as bob. This will not be successful since bob is not allowed to see alices salary.
  • Call the same endpoint (/finance/salary/alice) but login as dave. It should be successful since dave is from HR.