Search code examples
pythonnginxfastapireverse-proxysaml-2.0

FastAPI: SAML-SSO- The response was received at https://containerip/ instead of https:


I am facing a reverse proxy with nginx and FastAPI while integrating SAML SSO with OneLogin. I get the ERROR:routers.auth:Errors occurred: ['invalid_response'] followed by The response was received at https://containerip/ instead of https:myserverurl

Here's my nginx config

server {
    listen              443 ssl http2;
    listen              [::]:443 ssl http2;
    server_name         $hostname ;



    # SSL
    ssl_certificate     /etc/nginx/ssl/localhost.crt;
    ssl_certificate_key /etc/nginx/ssl/localhost.key;

    # security
    include             nginxconfig.io/security.conf;

    # restrict methods
    if ($request_method !~ ^(GET|POST)$) {
        return '405';
    }
    
    # New location for FastAPI-App
    location / {
        proxy_pass http://fastapi:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    
    }

This is how I am preparing the request for auth:

async def prepare_fastapi_request(request):
    # Convert FastAPI request to a format compatible with python3-saml    
    form_data = await request.form()
    server_port = request.url.port
    if server_port is None:
        server_port = "443" if request.url.scheme == "https" else "80"
    rv = {
        "https": "on" if request.url.scheme == "https" else "off",
        "http_host": request.client.host,
        "server_port": server_port,
        "script_name": request.url.path,
        "get_data": { },
        # "post_data": await request.form(),  # Changed this
        "post_data": { },  # Use this instead
    }
    if (request.query_params):
        rv["get_data"] = request.query_params,
    if "SAMLResponse" in form_data:
        SAMLResponse = form_data["SAMLResponse"]
        rv["post_data"]["SAMLResponse"] = SAMLResponse
    if "RelayState" in form_data:
        RelayState = form_data["RelayState"]
        rv["post_data"]["RelayState"] = RelayState
    return rv

This is the CMD to run my docker container on the server:

CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "5000", "--proxy-headers", "--forwarded-allow-ips", "*"]

I tried solutions using proxy headers, X-forwarded, proxy redirect, but nothing seems to work, I also changed the RelayState to the url of my service hosted on the server.


Solution

  • The problem was resolved by changing the following function

    async def prepare_fastapi_request(request):
    
    headers = request.headers
    forwarded_host = headers.get("x-forwarded-host")
    server_port = headers.get("x-forwarded-port")
    
    rv = {
        "https": "on" if request.url.scheme == "https" else "off",
        "http_host": forwarded_host,
        "server_port": server_port,
        "script_name": request.url.path,
        "get_data": request.query_params,
        "post_data": await request.form(),  # Changed this
        #"post_data": { },  # Use this instead
    }
    
    return rv