cancel
Showing results for 
Search instead for 
Did you mean: 

How to use XSUAA in FastAPI Python app

Mohan_Sharma
Product and Topic Expert
Product and Topic Expert
0 Kudos
220

Hi there, 

I have a fastapi app with 2 routes, one for rendering an html page and one for serving a request. I am using Jinja2 for templating.

Example of one of the route:

@router.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

My manifest.yaml for deploying it to cloud foundry:

---
applications:
- name: fastapi-app
  disk_quota: 2048M
  memory: 256M
  path: ./
  routes:
    -   route: fastapi-app.cfapps.eu10.hana.ondemand.com
  buildpacks:
  - python_buildpack
  command: uvicorn com.crack.snap.make.app:app --host 0.0.0.0 --port $PORT
  services:
    - app-xsuaa
    - app-logging-service
  logging:
    level: error
  env:
    PYTHONUNBUFFERED: true
    xsuaa_connectivity_instance_name: "app-xsuaa"
    xsuaa_destination_instance_name: "app-xsuaa"

How do I protect these fastapi routes directly using XSUAA, without having to create one more webapp then use app-router and then forwarding the request to fastapi app?

Also I want the fastapi to use the sub-account's default authentication which we do by using redirect-url of xs-security.json

{
	"xsappname": "fastapi-app",
	"tenant-mode": "dedicated",
	"scopes": [{
		"name": "$XSAPPNAME.fastapi_scope"
	}],
	"role-templates": [{
		"name": "FastAPIRoleTemplate",
		"default-role-name": "FastAPIRole",
		"description": "Role template for app users",
		"scope-references": ["$XSAPPNAME.fastapi_scope"]
	}
	],
	"oauth2-configuration": {
		"redirect-uris": [
			"https://*.cfapps.eu10.hana.ondemand.com/**"
		]
	}
}

Any help on achieving this will be really appreciated, we can also have a blog post on the same topic

SAP BTP, Cloud Foundry runtime and environment Python SAP BTP Security 

Accepted Solutions (0)

Answers (2)

Answers (2)

Mohan_Sharma
Product and Topic Expert
Product and Topic Expert

I did not find any straight forward solution to this, so I took a little turn around. Since we don't have `@sap/approuter` as a plugin for python/java applications yet. We will need to use Node for using the approuter.

Steps taken to achieve my goal:

1. Create a new node app for sap-approuter, check the below manifest.yaml 

- name: router
  routes:
    - route: fastapi-app.cfapps.eu10.hana.ondemand.com
  env:
    destinations: >
      [
       {
         "name":"fastapi-app",
          "url":"https://fastapi-srv.cfapps.eu10.hana.ondemand.com",
          "forwardAuthToken": true,
          "timeout": 600000
       }
      ]
  disk_quota: 256M
  timeout: 600
  memory: 256M
  path: web
  services:
    - a-xsuaa
    - a-logging-service

If you need more information on creating the node app, refer to this tutorial.

This destination will be used later to hit the FastAPI app through app-router app after authentication

2. Simple example xs-security.json for creating the xsuaa instance, where I will use our default IdP of the sub-account for authentication.

{
	"xsappname": "fastapi-app",
	"tenant-mode": "dedicated",
	"oauth2-configuration": {
		"redirect-uris": [
			"https://fastapi-app.cfapps.eu10.hana.ondemand.com/**"
		]
	}
}

 3. The magic of redirect resides in the xs-app.json, where i will redirect all incoming requests after authentication to FastAPI App. Whenever the user hits app-router app url `fastapi-app.cfapps.eu10.hana.ondemand.com`, which in my case gives an impression of FastAPI app url, I will simply redirect them to FastAPI's route which in turns returns the landing page for the user.

{
  "routes": [
    {
      "source": "/(.*)",
      "target": "$1",
      "destination": "fastapi-app",
      "httpMethods": ["GET"],
      "csrfProtection": false
    }
  ],
  "errorPage": [
    {
      "status": [403],
      "file": "resources/forbidden.html"
    },
    {
      "status": [404],
      "file": "resources/404.html"
    }
  ]
}

Here the destination should match the destination's name environment variable that we created in manifest.yaml that's how it know which server to call with the generated oauth token after authentication.

4. Example of the FastAPI route to return the landing page after authorization check

router = APIRouter()
templates = Jinja2Templates(directory="com/crack/snap/make/templates")


@router.get("/", response_class=HTMLResponse)
async def read_root(request: Request, security_context: Optional[SecurityContextXSUAA] = Depends(require_auth)):
	return templates.TemplateResponse("index.html", {"request": request})

5. I have skipped the role creation and role check for simplicity

 

gregorw
Active Contributor
0 Kudos

The tutorial Create an Application with Cloud Foundry Python Buildpack provides an example using Flask. Maybe you can adopt that to FastAPI. Would be great if you share it afterwards.

Mohan_Sharma
Product and Topic Expert
Product and Topic Expert
0 Kudos
@gregorw, thank you for the prompt help.. But the problem with the approach defined above is we need to create a separate webapp for user landing, then forward the user to the actual fastapi app. The fastapi alone is capable to having the frontend templates, then do we really need to have a separate node app(app router) just for landing the user?
gregorw
Active Contributor
Using the Approuter is the best practice. It implements the user authentication flow for you and you can use destinations to call the backend.