-
Notifications
You must be signed in to change notification settings - Fork 13
Home
This setup includes a local setup incl. a minimal FastAPI app with Authentication using Keycloak and Authorization using Open Policy Agent.
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).
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/).
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:
- Access Type: confidential
- Root URL: http://localhost:4000
- Valid Redirect URIs: http://localhost:4000/*
- Admin URL: http://localhost:4000
- Web Origins: http://localhost:4000/*
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
).
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.