Seeking Feedback: FastAPI with Kratos Authentication + Keto Authorisation #2766
0xliam
started this conversation in
Show and tell
Replies: 1 comment 3 replies
-
Update: Implemented a very basic Keto permissions check: from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import APIRouter, HTTPException, Depends
import ory_kratos_client
from ory_kratos_client.api import v0alpha2_api
from ory_kratos_client.model.submit_self_service_login_flow_body import (
SubmitSelfServiceLoginFlowBody,
)
from ory_kratos_client.exceptions import UnauthorizedException
import requests
KRATOS_API_READ_URL = "http://localhost:4433"
KETO_API_READ_URL = "http://localhost:4466"
KRATOS_CONFIGURATION = ory_kratos_client.Configuration(host=KRATOS_API_READ_URL)
KETO_CONFIGURATION = ory_kratos_client.Configuration(host=KETO_API_READ_URL)
router = APIRouter(prefix="/auth", tags=["auth"])
kratos = ory_kratos_client.ApiClient(KRATOS_CONFIGURATION)
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
async def get_current_active_user(token: str = Depends(oauth2_scheme)):
with kratos as api_client:
api_instance = v0alpha2_api.V0alpha2Api(api_client)
try:
session = api_instance.to_session(x_session_token=token)
except UnauthorizedException as e:
raise HTTPException(status_code=401)
if not session.active:
raise HTTPException(status_code=401)
return session
class KetoChecker:
def __init__(self, namespace: str, object: str, relation):
self.namespace = namespace
self.object = object
self.relation = relation
def __call__(self, session=Depends(get_current_active_user)):
# with httpx.AsyncClient() as client:
data = {
"namespace": self.namespace,
"object": self.object,
"relation": self.relation,
"subject_id": session.identity.id,
}
headers = {"content-type": "application/json"}
response = requests.post(
f"{KETO_API_READ_URL}/relation-tuples/check",
json=data,
headers=headers,
)
if not response.json().get("allowed"):
raise HTTPException(status_code=403)
@router.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
with kratos as api_client:
api_instance = v0alpha2_api.V0alpha2Api(api_client)
refresh = True
try:
flow = api_instance.initialize_self_service_login_flow_without_browser(
refresh=refresh
)
except ory_kratos_client.ApiException as e:
raise HTTPException(status_code=403, detail=e)
body = SubmitSelfServiceLoginFlowBody(
identifier=form_data.username,
password=form_data.password,
method="password",
)
try:
api_response = api_instance.submit_self_service_login_flow(
flow=flow.id, submit_self_service_login_flow_body=body
)
return {"access_token": api_response.session_token, "token_type": "bearer"}
except ory_kratos_client.ApiException as e:
raise HTTPException(status_code=403, detail=e)
secret_auth = KetoChecker(namespace="auth", object="secret", relation="read")
@router.get("/secret")
async def authenticated_route(auth: secret_auth = Depends()):
return {"secret": "message"} |
Beta Was this translation helpful? Give feedback.
3 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Hi there,
I am looking for feedback on a FastAPI integration I've written to secure API endpoints and authenticate against Kratos.
My FastAPI application currently just consists of a few CRUD functions, with no authentication.
Another application that runs on customer premises needs to interact with this FastAPI service, and I wanted to add a layer of authentication and authorisation.
I plan on using Keto for authorisation, to ensure users of a customer/tenant can only run CRUD functions on their own objects, and will likely adapt this guide for Flask to FastAPI's API.
Hopefully that makes sense!
Here is my authorisation code - feedback is much appreciated - I haven't implemented any authorisation yet - just checking that a user has a valid session token and then using that session to allow access to a resource.
I've implemented two endpoints:
Feedback would be much appreciated.
Beta Was this translation helpful? Give feedback.
All reactions