Coder Social home page Coder Social logo

Security scopes about fastapi HOT 6 CLOSED

mattlaue avatar mattlaue commented on May 8, 2024 1
Security scopes

from fastapi.

Comments (6)

tiangolo avatar tiangolo commented on May 8, 2024 3

Here it is! 🎉 🍰 🚀

https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/


@mattlaue the problem is that you probably still want to read the token, at least at some point, to verify that it's valid.

But the new SecurityScopes allows you to read the scopes in a dependency that can be used in other dependencies or path operations.

Those dependencies and path operations can declare their own required scopes. And the sub-dependency (that is probably reading the token and verifying it directly) can access all those required scopes from the dependants.


So, you can have a central point that checks and verifies all the scopes, and then in different path operations you can have Security dependencies with different scopes, for example:

@app.get("/somepath1/")
def some_path_1(current_user: User = Security(get_current_user, scopes=["some", "scopes"])):
    ...

@app.get("/somepath2/")
def some_path_2(current_user: User = Security(get_current_user, scopes=["something", "different"])):
    ...

def other_dependency(current_user: User = Security(get_current_user, scopes=["subdependency", "required"])):
    ...

@app.get("/somepath3/")
def some_path_3(sub_dep = Depends(other_dependency), current_user: User = Security(get_current_user, scopes=["more"])):
    ...

from fastapi.

kthwaite avatar kthwaite commented on May 8, 2024

This is a good question, and it isn't clear to me either. The advanced dependencies page in the docs would seem to suggest a pattern where you create a callable class or higher-order-function initialised from a list of scopes with a dependency on Security, and use that to check scope, e.g.

class ScopeChecker:
    def __init__(self, scopes):
        self._scopes = scopes

    def __call__(self, request: Request, token: str = Security(oauth2_scheme)):
        token = validate_jwt_token(token)  # proper validation goes here
        if not token_has_required_scopes(token, self._scopes):
            raise HTTPException(403, detail='Forbidden')
        return token

but that either leads to a rather ugly syntax, or you have to create a global dep object for each set of scopes (!):

# either
@app.get('/')
async def root(request: Request, user: UserToken = Depends(ScopeChecker(['user']))):
    # ...

# or 

check_is_user = ScopeChecker(['user'])

@app.get('/')
async def root(request: Request, user: UserToken = Depends(check_is_user)):
    # ...

Based on my (cursory) reading of fastapi.dependencies.utils I've been subclassing Depends for something roughly like the following:

class ScopedTo(Depends):
    """Dependency on particular scope.
    """
    def __init__(self, *scopes) -> None:
        super().__init__(self.__call__)
        self._scopes = scopes

    def __call__(self, request: Request, token: str = Security(oauth2_scheme)) -> TokenData:
        """Check scopes and return the current user.
        """
        token = validate_jwt_token(token) # proper validation goes here
        if not token_has_required_scopes(token, self._scopes):
            raise HTTPException(403, detail='Forbidden')
        return token

which can then be used like:

@app.get('/')
async def root(request: Request, user: UserToken = ScopedTo('user', 'root:read')):
    # ...

But I'm not convinced that this is the best solution. I would welcome any clarification on this issue!

from fastapi.

tiangolo avatar tiangolo commented on May 8, 2024

Sorry for the delay guys.

You're right, currently, there's no way to access the scopes defined in a Security inside of its dependency/security function. And it totally makes sense to be able to access them.

I'm thinking about implementing it as an additional parameter that you can declare in the security function, I imagine a class fastapi.security.Scopes that has an attribute scopes with the list of scopes declared.

So, you would define your dependency something like:

async def get_current_user(scopes: Scopes, token: str = Security(oauth2_scheme)):
    for scope in scopes.scopes:
        ...

It has to be a predefined class instead of a plain list to be able to recognize it and solve it while solving the dependencies. But I think that would be the simplest/cleanest way to get the scopes inside your dependencies without breaking or compromising anything else.

Would that sound good for you?

from fastapi.

mattlaue avatar mattlaue commented on May 8, 2024

This implementation is almost exactly the same as what we implemented for our application. I'd suggest something like (if possible):

async def get_current_user(scopes: Scopes(Security(oauth2_scheme))):
    for scope in scopes.scopes:
        ...

This way you don't have an unused parameter token cluttering the function.

from fastapi.

tiangolo avatar tiangolo commented on May 8, 2024

I assume this is solved now, so I'll close this issue. But feel free to add more comments or create new issues.

from fastapi.

lsaint avatar lsaint commented on May 8, 2024

Is there any mechanism to overwrite security scopes?
I had 10 URL in a router:

router = Router(dependencies=[Security(XXXBearer(), scopes=["admin"])])

@router.post("/url1/")
def url1_func():
    ...

@router.post("/url2/")
def url2_func():
    ...

@router.post("/url3/")
def url2_func():
    ...

...

let's say, the url3's scopes only require ["user"],
how should I do?

@router.post("/url3/")
def url2_func(a = Security(XXXBearer(), scopes=["user"])):
    # it did not work like this
    # the require scopes were still ["admin"]

from fastapi.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.