Understanding Role-based Access Control (RBAC) in FastAPI with JWT Authentication

In modern web applications, security is paramount, especially when dealing with sensitive user data or performing critical operations. Role-based Access Control (RBAC) is a popular approach to manage access permissions effectively. Combining RBAC with JSON Web Token (JWT) authentication can provide a robust security mechanism for FastAPI applications. In this blog post, we'll delve into a Python code snippet that implements RBAC with JWT authentication in a FastAPI application.


Understanding Role-based Access Control in FastAPI with JWT Authentication


def requires(category, roles):
    def decorator(func):
        @wraps(func)
        async def wrapper(request: Request, *args, **kwargs):
            authorizationToken = request.headers.get("authorization", "")
            # FOR hasini add a decrypt logic
            
            scheme, credentials = (
                authorizationToken.split(" ", 1)
                if " " in authorizationToken
                else ("", "")
            )
            if not scheme == "Bearer":
                raise HTTPException(
                    status_code=403, detail="Invalid authentication scheme."
                )

            decoded_token = decode_token(credentials)

            if not decoded_token:
                raise HTTPException(status_code=401, detail="Invalid token")

            glbId = decoded_token["UserId"] + "_" + decoded_token["infoId"]
            userInfoObj = glblobj.user.get(glbId, {})
            tokenValid = userInfoObj.get("tokenActive", False)

            try:
                payload = await request.json()
            except json.decoder.JSONDecodeError as e:
                payload = {}
            type_value = payload.get("type") if category == "$type" else category

            permissions = userInfoObj.get("Permissions", {}).get(type_value, {})

            if not any(permissions[permission_order.index(role)] == 1 for role in roles):
                raise HTTPException(status_code=403, detail="Insufficient privileges")
            if "activeUser" in func.__code__.co_varnames:
                user = {}  # Replace this with the actual user information
                user.update(userInfoObj)
                user.update(decoded_token)
                kwargs["activeUser"] = user
            if "authorizationToken" in func.__code__.co_varnames:
                kwargs["authorizationToken"] = authorizationToken
            return await func(request, *args, **kwargs)

        return wrapper

    return decorator


Understanding the Code

Let's break down the provided Python code:

  1. Imports: The code imports necessary modules and libraries such as functools, json, FastAPI, Request, Depends, HTTPException, HTTPBearer, jwt, config from decouple, and custom utility and global class modules.
  2. Constants Initialization: It initializes constants such as JWT_SECRET, JWT_ALGORITHM, ACCESS_TOKEN_EXPIRES_IN, and REFRESH_TOKEN_EXPIRES_IN using environment variables.
  3. requires Decorator: This decorator function facilitates role-based authorization. It takes two parameters: category (which can be either a string or "$type") and roles (a list of roles). It wraps the API endpoint functions to enforce role-based authorization.
  4. decode_token Function: This function decodes the JWT token. It verifies the token's authenticity using the provided secret and algorithm. If the token is valid and not expired, it returns the decoded token; otherwise, it returns None.
  5. Wrapper Function: This function is generated by the requires decorator. It intercepts the incoming requests, checks the authorization token, decodes it, verifies the user's permissions against the specified roles, and then calls the wrapped function if authorization is successful.

Usage Example

@router.get("/", tags=["Company Profile"])
@requires("accounts", ["update"])
async def getCompanyDetails(request: Request):
    return await CompanyModule.get()


In this example, the getCompanyDetails function is decorated with @requires("accounts", ["update"]), indicating that only users with the "update" role in the "accounts" category can access this endpoint. When a request is made to this endpoint, the requires decorator intercepts it, verifies the user's permissions, and only allows access if the user meets the specified criteria.

Conclusion

Implementing role-based access control with JWT authentication adds an extra layer of security to FastAPI applications, ensuring that only authorized users can access certain endpoints or perform specific actions. By understanding the provided code snippet and its usage, developers can effectively integrate RBAC into their FastAPI projects, safeguarding sensitive data and maintaining the integrity of their applications.

Previous Post Next Post

Contact Form