-
Is your feature request related to a problem? Please describe. Describe the solution you'd like from fastapi import Depends, FastAPI
@app = FastAPI()
@app.get(
path='/',
requires=['secret_permission']
)
def my_endpoint(current_user: User = Security(custom_auth_handler)) -> str:
return f'{current_user} is authorized!' When a given user does not have the specified permission, a 401 should be raised. Additionally, if the user is not authenticated, it should redirect to the login endpoint before throwing a 401. Describe alternatives you've considered Additional context |
Beta Was this translation helpful? Give feedback.
Replies: 13 comments
-
Hmm, the authentication mechanisms included in Starlette are a little bit more fixed. For example, it is assumed that there is a
In FastAPI, to define different permission levels you would probably use the dependency injection system (and the security system): https://fastapi.tiangolo.com/tutorial/security/get-current-user/ With that, you can do any check you need in a dependency and handle permissions directly, you don't have to communicate the approval/disapproval using a list of strings. And it also would apply for authenticating things that are not necessarily users, like third party applications. For a more complete example. for example, requiring that a there should be a user logged in, that the user should be active and that it should be a "superuser", see the project generators: https://fastapi.tiangolo.com/project-generation/ For example, this dependency in this line requires exactly that I just described: https://github.com/tiangolo/full-stack-fastapi-postgresql/blob/master/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/app/api/api_v1/endpoints/user.py#L38 Using the same mechanism, you could restrict with way more complex logic. For example, "only a user that has more than 100 items can access this endpoint". |
Beta Was this translation helpful? Give feedback.
-
Thanks @tiangolo I've managed to get a functional prototype using something similar to what you mentioned: from typing import Set, List
import uuid
from pydantic import BaseModel, Schema
from starlette.requests import Request
from starlette.exceptions import HTTPException
from starlette.status import HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN
from fastapi import FastAPI, Depends, Header
# define User object
class User(BaseModel):
id: uuid.UUID = Schema(
default=...,
description='Identifier for this User'
)
username: str = Schema(
default=...,
description='Username for this User'
)
roles: Set[str] = Schema(
default=set(),
description='Roles assigned to this User'
)
# mock repository lookup
_db = dict(
validUser=User(
id=uuid.uuid4(),
username='validUser',
roles=set(['valid_role'])
),
badUser=User(
id=uuid.uuid4(),
username='badUser',
roles=set()
)
)
# define header authentication
class HTTPHeaderAuthentication:
def __init__(self, *, scopes: List[str]):
self.scopes = set(scopes)
async def __call__(self, request: Request, authuser: str = Header(None)) -> User:
user = self.locate_user(username=authuser)
if not user:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Not authenticated"
)
if not self.has_required_scope(user.roles):
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED, detail=f"{user.username} is not authorized to access this endpoint"
)
return user
def locate_user(self, username: str) -> User:
"""Mock lookup in repository"""
return _db.get(username, None)
def has_required_scope(self, user_scopes: Set[str]) -> bool:
"""Verify the user has the desired auth scope"""
for scope in self.scopes:
if scope not in user_scopes:
return False
return True
# create FastAPI object
app = FastAPI()
# setup protected endpoint
@app.get(
path='/'
)
def root(current_user: User = Depends(HTTPHeaderAuthentication(scopes=['valid_role']))) -> User:
return current_user
if __name__ == '__main__':
import uvicorn
app.debug = True
uvicorn.run(app, host="127.0.0.1", port=5000) validUser has the required scope roles to access the endpoint, while badUser does not. Though it's not as elegant as the Starlette AuthenticationMiddleware. Do you have any suggestion on how to write custom authentication handlers in FastAPI? |
Beta Was this translation helpful? Give feedback.
-
Cool! Yes, I have suggestions, you don't have to create a full If you want to use something pre-defined, you can import and use the OAuth2 classes that do more or less the same things, but based on a standard: https://fastapi.tiangolo.com/tutorial/security/get-current-user/ ...and then you can also say your API is using a fancy security/authentication standard (OAuth2), the same used by Google, Facebook, etc. 😉 About having different requirements per path operation, check the project generators, for example, there are:
You could do the same to ensure some specific roles. In fact, that's how it's done in the Couchbase project generator: https://github.com/tiangolo/full-stack-fastapi-couchbase |
Beta Was this translation helpful? Give feedback.
-
As I understand you were able to solve your problem I'll close this issue now. But feel free to add more comments or create new issues. |
Beta Was this translation helpful? Give feedback.
-
I just got bitten by the incompatibility, too. Implemented a nice authorization middleware and then realized it doesn't work. Would it be possible to add a section to the documentation that lists which features of Starlette FastAPI is not compatible with? |
Beta Was this translation helpful? Give feedback.
-
Just to clarify, you can use @Midnighter you should be able to use all the parts of the middleware exactly the same, even with If you have something that expects to work with Starlette's If you have a specific case that you need to solve with |
Beta Was this translation helpful? Give feedback.
-
My apologies, I wasn't clear. I indeed meant the
I have already re-written it to use dependency injection and it works quite beautifully. I just wanted to suggest documenting the little parts that don't work well together such as |
Beta Was this translation helpful? Give feedback.
-
Thanks @Midnighter . I'm glad it's working well for you now. I'm not sure what else might not work right away (I don't think there's much more). This is mainly becase Starlette also moves quite fast, for example, when FastAPI was created, there was not authentication middleware in Starlette... Maybe something we could do is to document how to create a That's something that could also go in a blog post 😉 |
Beta Was this translation helpful? Give feedback.
-
I'm also facing same issue, I partially solved it with security but I think middleware approach is more elegant way to fix it. My use case: I'm using istio authentication. My token is already verified before it reaches to the application layer. In application layer all I need to check if authorization logic using scope. Expected flow:
app.add_middleware(AuthenticationMiddleware, backend=CustomAuthenticationBackend()) Which basically add a user(type of starlette.authentication.BaseUser) and auth object(starlette.authentication.AuthCredentials)
Implementing another get user or scope checking with Security function is unnecessary at this point. As user is already inside request and writing another function to validate scope manually is unnecessary. 2nd problem, When I'm using Security, openapi docs not reflecting the authentication mechanism in the UI |
Beta Was this translation helpful? Give feedback.
-
@tiangolo Is this feature added to fastapi already? Since this issue was created more than a year ago. Not sure if a should create new issue! Can you take a look at my comment please, if it makes sense and you have any suggestion? |
Beta Was this translation helpful? Give feedback.
-
@apocalypse32 I would suggest you use the integrated tools in FastAPI, that would work better: https://fastapi.tiangolo.com/tutorial/security/get-current-user/ |
Beta Was this translation helpful? Give feedback.
-
Assuming the original need was handled, this will be automatically closed now. But feel free to add more comments or create new issues or PRs. |
Beta Was this translation helpful? Give feedback.
-
If it helps anyone 4 years later, this is what I'm currently testing... Same problem: authorized scopes already present in request from Starlette AuthenticationMiddleware and just want a simple
|
Beta Was this translation helpful? Give feedback.
Hmm, the authentication mechanisms included in Starlette are a little bit more fixed.
For example, it is assumed that there is a
user
entity with two fixed attributes:.is_authenticated
.display_name
In FastAPI, to define different permission levels you would probably use the dependency injection system (and the security system): https://fastapi.tiangolo.com/tutorial/security/get-current-user/
With that, you can do any check you need in a dependency and handle permissions directly, you don't have to communicate the approval/disapproval using a list of strings. And it also would apply for authenticating things that are not necessarily users, like third party applications.
For a more com…