FastAPI is a modern web framework for building APIs in Python. It’s one of my personal favorite web frameworks as it has built-in support for OpenAPI specs (meaning you can write your backend code and generate everything from it) and it supports dependency injection.
In this post, we’ll briefly look at how FastAPI’s Depends works. We’ll then see why it applies so well to authentication and authorization. We’ll also contrast it with middleware, which is another common option for auth. Finally, we’ll look at some more advanced patterns for authorization in FastAPI.
What is Dependency Injection?
One of FastAPI’s more powerful features is its first class support for dependency injection. We have a longer guide here, but let’s look at a quick example of how it can be used.
Let’s say we are building a paginated API. Each API call may include a page_number and a page_size. Now, we could just create an API and take these parameters in directly:
@app.get("/things/") async def fetch_things(page_number: int = 0, page_size: int = 100): return db.fetch_things(page_number, page_size)
But, we probably want to add some validation logic so no one asks for page_number -1 or page_size 10,000,000.
@app.get("/things/") async def fetch_things(page_number: int = 0, page_size: int = 100): if page_number < 0: raise HTTPException(status_code=400, detail="Invalid page number") elif page_size <= 0: raise HTTPException(status_code=400, detail="Invalid page size") elif page_size > 100: raise HTTPException(status_code=400, detail="Page size can be at most 100") return db.fetch_things(page_number, page_size)
And this is... fine, but if we had 10 APIs or 100 APIs that all needed the same paging params, it’d get a bit tedious. This is where dependency injection comes in - we can move all this logic into a function and inject that function into our API:
async def paging_params_dep(page_number: int = 0, page_size: int = 100): if page_number < 0: raise HTTPException(status_code=400, detail="Invalid page number") elif page_size <= 0: raise HTTPException(status_code=400, detail="Invalid page size") elif page_size > 100: raise HTTPException(status_code=400, detail="Page size can be at most 100") return PagingParams(page_number, page_size) @app.get("/things/") async def fetch_things(paging_params: PagingParams = Depends(paging_params_dep)): return db.fetch_things(paging_params) @app.get("/other_things/") async def fetch_other_things(paging_params: PagingParams = Depends(paging_params_dep)): return db.fetch_other_things(paging_params)
This has some nice benefits:
Each route that takes in PagingParams is automatically validated and has default values.
It’s less verbose and error-prone than having the first line of each route be validate_paging_params(page_number, page_size)
This still works with FastAPI’s OpenAPI support - those parameters will show up in your OpenAPI specs.
What does this have to do with authentication?
It turns out, this is also a great way to model auth! Imagine you had a function like:
async def validate_token(token: str): try: # This could be JWT validation, looking up a session token in the DB, etc. return await get_user_for_token(token) except: return None
To hook this up to an API route, all we’d need to do is wrap it in a dependency:
async def require_valid_token_dep(req: Request): # This could also be a cookie, x-api-key header, etc. token = req.headers["Authorization"] user = await validate_token(token) if user == None: raise HTTPException(status_code=401, detail="Unauthorized") return user
And then all of our protected routes can add this dependency:
@app.get("/protected") async def do_secret_things(user: User = Depends(require_valid_token_dep)): # do something with the user
If the user provides a valid token, this route will run and user is set. Otherwise, a 401 will be returned.
Note: OpenAPI/Swagger does have first-class support for specifying auth tokens, but you have to use one of the dedicated classes for it. Instead of req.headers["Authorization"], you can use HTTPBearer(auto_error=False) from fastapi.security which returns an HTTPAuthorizationCredentials.
Middleware vs Depends for Auth
FastAPI, like most frameworks, has a concept of middleware. Your middleware can contain code that will run before and after a request. It can modify the request before the request gets to your route and it can modify the response before it’s returned to the user.
In many other frameworks, middleware is a really common place for authentication checks to take place. However, that’s often because the middleware is also tasked with “injecting” the user into the route. For example, a common pattern in Express is to do something like:
app.get("/protected", authMiddleware, (req, res) => { // req.user is set by the middleware // as there's no good way to pass in extra information into this route, // outside of the request });
Since FastAPI has a built-in concept of injection, you may not need to use middleware at all. I’d consider using middleware if you need to periodically “refresh” your auth tokens (to keep them alive) and set the response as a cookie.
In this case, you’ll want to use request.state to pass information from the middleware to the routes (and you can use a dependency to validate the request.state if you’d like).
Otherwise, I’d stick with using Depends as the user will be injected directly into your routes without needing to go through request.state.
Authorization - Multi-tenancy, Roles & Permissions
If we apply everything we learned so far, adding in multi-tenancy, roles or permissions can be pretty straightforward. Let’s say we have a unique subdomain for each of our customers, we can make a dependency for this subdomain:
async def tenant_by_subdomain_dep(request: Request) -> Optional[str]: # first we get the subdomain from the host header host = request.headers.get("host", "") parts = host.split(".") if len(parts) <= 2: raise HTTPException(status_code=404, detail="Not found") subdomain = parts[0] # then we lookup the tenant by subdomain tenant = await lookup_tenant_for_subdomain(subdomain) if tenant == None: raise HTTPException(status_code=404, detail="Not found") return tenant
We can combine this idea with our previous ideas and make a new “multi-tenant” dependency:
async def get_user_and_tenant_for_token( user: User = Depends(require_valid_token_dep), tenant: Tenant = Depends(tenant_by_subdomain_dep), ) -> UserAndTenant: is_user_in_tenant = await check_user_is_in_tenant(tenant, user) if is_user_in_tenant: return UserAndTenant(user, tenant) raise HTTPException(status_code=403, detail="Forbidden")
We can then inject this dependency into our routes:
@app.get("/protected") async def do_secret_things(user_and_tenant: UserAndTenant = Depends(get_user_and_tenant_for_token)): # do something with the user and tenant
And this ends up doing a few major things:
Checking that the user has a valid token
Checking that the user is making a request to a valid subdomain
Checking that the user should have access to that subdomain
If any of those invariants aren’t met - an error is returned and our route will never run. We can extend this to include other things like roles & permissions (RBAC) or making sure the user has a certain property set (active paid subscription vs no active subscription).
PropelAuth <3 FastAPI
At PropelAuth, we’re big fans of FastAPI. We have a FastAPI library that will enable you to set up authentication and authorization quickly - including SSO, Enterprise SSO / SAML, SCIM Provisioning, and more.
And it all works with dependencies like the ones you’ve seen above, e.g.:
@app.get("/") async def root(current_user: User = Depends(auth.require_user)): return {"message": f"Hello {current_user.user_id}"}
You can find out more here.
Summary
FastAPI's dependency injection provides a powerful way to handle authentication and authorization in web applications.
The Depends feature allows for clean, reusable code for validating tokens, checking user permissions, and handling multi-tenancy.
Compared to middleware, using dependencies for auth offers more flexibility and direct integration with route functions.
Complex authorization scenarios like multi-tenancy and role-based access control can be efficiently implemented using nested dependencies.
PropelAuth offers a FastAPI library that simplifies the implementation of advanced authentication and authorization features.
以上是FastAPI Auth with Dependency Injection的详细内容。更多信息请关注PHP中文网其他相关文章!

Linux终端中查看Python版本时遇到权限问题的解决方法当你在Linux终端中尝试查看Python的版本时,输入python...

本文解释了如何使用美丽的汤库来解析html。 它详细介绍了常见方法,例如find(),find_all(),select()和get_text(),以用于数据提取,处理不同的HTML结构和错误以及替代方案(SEL)

Python 对象的序列化和反序列化是任何非平凡程序的关键方面。如果您将某些内容保存到 Python 文件中,如果您读取配置文件,或者如果您响应 HTTP 请求,您都会进行对象序列化和反序列化。 从某种意义上说,序列化和反序列化是世界上最无聊的事情。谁会在乎所有这些格式和协议?您想持久化或流式传输一些 Python 对象,并在以后完整地取回它们。 这是一种在概念层面上看待世界的好方法。但是,在实际层面上,您选择的序列化方案、格式或协议可能会决定程序运行的速度、安全性、维护状态的自由度以及与其他系

本文比较了Tensorflow和Pytorch的深度学习。 它详细介绍了所涉及的步骤:数据准备,模型构建,培训,评估和部署。 框架之间的关键差异,特别是关于计算刻度的

Python的statistics模块提供强大的数据统计分析功能,帮助我们快速理解数据整体特征,例如生物统计学和商业分析等领域。无需逐个查看数据点,只需查看均值或方差等统计量,即可发现原始数据中可能被忽略的趋势和特征,并更轻松、有效地比较大型数据集。 本教程将介绍如何计算平均值和衡量数据集的离散程度。除非另有说明,本模块中的所有函数都支持使用mean()函数计算平均值,而非简单的求和平均。 也可使用浮点数。 import random import statistics from fracti

该教程建立在先前对美丽汤的介绍基础上,重点是简单的树导航之外的DOM操纵。 我们将探索有效的搜索方法和技术,以修改HTML结构。 一种常见的DOM搜索方法是EX

本文讨论了诸如Numpy,Pandas,Matplotlib,Scikit-Learn,Tensorflow,Tensorflow,Django,Blask和请求等流行的Python库,并详细介绍了它们在科学计算,数据分析,可视化,机器学习,网络开发和H中的用途

本文指导Python开发人员构建命令行界面(CLIS)。 它使用Typer,Click和ArgParse等库详细介绍,强调输入/输出处理,并促进用户友好的设计模式,以提高CLI可用性。


热AI工具

Undresser.AI Undress
人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover
用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool
免费脱衣服图片

Clothoff.io
AI脱衣机

AI Hentai Generator
免费生成ai无尽的。

热门文章

热工具

SublimeText3 英文版
推荐:为Win版本,支持代码提示!

mPDF
mPDF是一个PHP库,可以从UTF-8编码的HTML生成PDF文件。原作者Ian Back编写mPDF以从他的网站上“即时”输出PDF文件,并处理不同的语言。与原始脚本如HTML2FPDF相比,它的速度较慢,并且在使用Unicode字体时生成的文件较大,但支持CSS样式等,并进行了大量增强。支持几乎所有语言,包括RTL(阿拉伯语和希伯来语)和CJK(中日韩)。支持嵌套的块级元素(如P、DIV),

适用于 Eclipse 的 SAP NetWeaver 服务器适配器
将Eclipse与SAP NetWeaver应用服务器集成。

SublimeText3 Mac版
神级代码编辑软件(SublimeText3)

螳螂BT
Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。