Luiz Tauffer
Luiz Tauffer

Reputation: 632

FastAPI auth check before granting access to sub-applications

I am mounting a Flask app as a sub-application in my root FastAPI app, as explained in the documentation

Now I want to add an authentication layer using HTTPAuthorizationCredentials dependency, as nicely explained in this tutorial

tutorial code

How can I do that?

Preferably, I would like that any type of access attempt to my Flask sub-application goes first through a valid token authentication process implemented in my FastAPI root app. Is that possible?

Upvotes: 5

Views: 1220

Answers (1)

John S John
John S John

Reputation: 422

You can use a custom WSGIMiddleware and authorize the call to flask app inside that like this:

from fastapi import FastAPI, Depends, HTTPException
from fastapi.middleware.wsgi import WSGIMiddleware
from flask import Flask, escape, request
from starlette.routing import Mount
from starlette.types import Scope, Receive, Send

flask_app = Flask(__name__)

def authenticate(authorization: str = Header()):
    # Add logic to authorize user
    if authorization == "VALID_TOKEN":
        return
    else:
        raise HTTPException(status_code=401, detail="Not Authorized")

class AuthWSGIMiddleware(WSGIMiddleware):

    async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
        _, authorization = next((header for header in scope['headers'] if header[0] == b'authorization'), (b'authorization', "" ))
        authenticate(authorization.decode('utf-8'))
        await super().__call__(scope, receive, send)

routes = [
            Mount("/v1", AuthWSGIMiddleware(flask_app)),
        ]

# OR Optionally use this as you were doing
# The above one is preferred as per starlette docs
# app.mount("/v1", WSGIMiddleware(flask_app))


@flask_app.route("/")
def flask_main():
    name = request.args.get("name", "World")
    return f"Hello, {escape(name)} from Flask!"

app = FastAPI(routes=routes, dependencies=[Depends(authenticate)])


@app.get("/v2")
def read_main():
    return {"message": "Hello World"}

For the tutorial you are looking at, you can replace the invoking authenticate function in my example inside AuthWSGIMiddleware->__call__() with

AuthHandler().decode_toke(authorization)

Upvotes: 3

Related Questions