A FastAPI Plug-In to support authentication authorization using the Microsoft Authentication Library (MSAL)

Overview

FastAPI/MSAL - MSAL (Microsoft Authentication Library) plugin for FastAPI

Checked with mypy Code style: black Lint & Security Download monthly

FastAPI - https://github.com/tiangolo/fastapi FastAPI is a modern, fast (high-performance), web framework for building APIs based on standard Python type hints.

MSAL for Python - https://github.com/AzureAD/microsoft-authentication-library-for-python The Microsoft Authentication Library for Python enables applications to integrate with the Microsoft identity platform. It allows you to sign in users or apps with Microsoft identities and obtain tokens to call Microsoft APIs such as Microsoft Graph or your own APIs registered with the Microsoft identity platform. It is built using industry standard OAuth2 and OpenID Connect protocols

The fastapi_msal package was built to allow quick "out of the box" integration with MSAL. As a result the pacage was built around simplicity and ease of use on the expense of flexability and versatility.

Features

  1. Includes Async implementation of MSAL confidential client class utilizaing Starlette threadpool model.
  2. Use pydantic models to translate the MSAL objects to data objects which are code and easy to work with.
  3. Have a built-in router which includes the required paths for the authentication flow.
  4. Include a dependency class to authenticate and secure your application APIs
  5. Includes a pydantic setting class for easy and secure configuration from your ENV (or .env or secrets directory)
  6. Full support with FastAPI swagger documentations and authentication simulation

Installation

pip install "fastapi_msal"

Or if you wish to have all the required packages straight forward

pipenv install "fastapi_msal[full]"

Prerequisets

  1. Python 3.7 and above
  2. As part of your fastapi application the following packages should be included:
    (if you use the [full] method it is not required.)
    1. python-multipart, From FastAPI documentation: This is required since OAuth2 (Which MSAL is based upon) uses "form data" to send the credentials.

    2. itsdangerous Used by Starlette session middleware

Usage

  1. Follow the application registration process with the microsoft identity platform. Finishing the processes will allow you to retrieve your app_code and app_credentials (app_secret) As well as register your app callback path with the platform.

  2. Create a new main.py file and add the following lines. Make sure to update the lines with the information retrieved in the previous step

import uvicorn
from fastapi import FastAPI, Depends
from starlette.middleware.sessions import SessionMiddleware
from fastapi_msal import MSALAuthorization, UserInfo, MSALClientConfig

client_config: MSALClientConfig = MSALClientConfig()
client_config.client_id = "The Client ID rerived at step #1"
client_config.client_credential = "The Client secret retrived at step #1"
client_config.tenant = "Your tenant id"

app = FastAPI()
app.add_middleware(SessionMiddleware, secret_key="SOME_SSH_KEY_ONLY_YOU_KNOW")  # replace with your own!!!
msal_auth = MSALAuthorization(client_config=client_config)
app.include_router(msal_auth.router)


@app.get("/users/me", response_model=UserInfo, response_model_exclude_none=True, response_model_by_alias=False)
async def read_users_me(current_user: UserInfo = Depends(msal_auth.scheme)) -> UserInfo:
    return current_user


if __name__ == "__main__":
    uvicorn.run("main:app", host="localhost", port=5000, reload=True)
  1. Run your app
(pipenv shell)$ python main.py
INFO:     Uvicorn running on http://localhost:5000 (Press CTRL+C to quit)
INFO:     Started reloader process [12785] using statreload
INFO:     Started server process [12787]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
  1. Browse to http://localhost:5000/docs - this is the API docs generated by FastAPI (totaly cool!) Document Page Image

  2. Using the "built-in" authenticaiton button (the little lock) you will be able to set the full authentication process Authorize Page Image (Igonre the cline_id and client_secret - they are not relevant for the process as you already set them)

  3. After you complete the process you will get a confirmation popup Token Page Image

  4. Trying out the ME api endpoint Me Page Image

Working Example/Template

If you wish to try out a working example, clone the following project and adjust it to your needs: https://github.com/dudil/ms-identity-python-webapp

NB! Make sure you are using the fastapi_msal branch!!!

TODO List

  • Add support for local/redis session cache
  • Add Tests
  • Proper Documentation
Comments
  • Question: How to access API outside of swagger page

    Question: How to access API outside of swagger page

    Hello,

    My exploration into this package is going well, but I have a few more questions as I try to get an optimal experience. Thanks for bearing with me :).

    I would like to be able to call the API outside of the swagger page (e.g. from the command line with curl).

    I am getting:

    2021-06-04T21:50:33.804149794Z [2021-06-04 21:50:33 +0000] [7] [ERROR] Exception in ASGI application 2021-06-04T21:50:33.804248696Z Traceback (most recent call last): 2021-06-04T21:50:33.804263696Z File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 371, in run_asgi 2021-06-04T21:50:33.804271997Z result = await app(self.scope, self.receive, self.send) 2021-06-04T21:50:33.804279297Z File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 59, in call 2021-06-04T21:50:33.804286697Z return await self.app(scope, receive, send) 2021-06-04T21:50:33.804293597Z File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in call 2021-06-04T21:50:33.804300897Z await super().call(scope, receive, send) 2021-06-04T21:50:33.804321397Z File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 111, in call 2021-06-04T21:50:33.804329098Z await self.middleware_stack(scope, receive, send) 2021-06-04T21:50:33.804336098Z File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in call 2021-06-04T21:50:33.804343398Z raise exc from None 2021-06-04T21:50:33.804350098Z File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in call 2021-06-04T21:50:33.804362998Z await self.app(scope, receive, _send) 2021-06-04T21:50:33.804369798Z File "/usr/local/lib/python3.9/site-packages/starlette/middleware/sessions.py", line 75, in call 2021-06-04T21:50:33.804388099Z await self.app(scope, receive, send_wrapper) 2021-06-04T21:50:33.804395799Z File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in call 2021-06-04T21:50:33.804403099Z raise exc from None 2021-06-04T21:50:33.804410099Z File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in call 2021-06-04T21:50:33.804514001Z await self.app(scope, receive, sender) 2021-06-04T21:50:33.804522001Z File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 566, in call 2021-06-04T21:50:33.804529501Z await route.handle(scope, receive, send) 2021-06-04T21:50:33.804536701Z File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 227, in handle 2021-06-04T21:50:33.804544002Z await self.app(scope, receive, send) 2021-06-04T21:50:33.804551002Z File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 41, in app 2021-06-04T21:50:33.804558302Z response = await func(request) 2021-06-04T21:50:33.804578902Z File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 191, in app 2021-06-04T21:50:33.804587002Z solved_result = await solve_dependencies( 2021-06-04T21:50:33.804594103Z File "/usr/local/lib/python3.9/site-packages/fastapi/dependencies/utils.py", line 548, in solve_dependencies 2021-06-04T21:50:33.804603703Z solved = await call(**sub_values) 2021-06-04T21:50:33.804610803Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/security/msal_scheme.py", line 47, in call 2021-06-04T21:50:33.804618203Z token_claims: Optional[IDTokenClaims] = await self.handler.parse_id_token( 2021-06-04T21:50:33.804625203Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/security/msal_auth_code_handler.py", line 68, in parse_id_token 2021-06-04T21:50:33.804632603Z auth_token: Optional[AuthToken] = await self.get_token_from_session( 2021-06-04T21:50:33.804639803Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/security/msal_auth_code_handler.py", line 89, in get_token_from_session 2021-06-04T21:50:33.804647304Z return await AuthToken.load_from_session( 2021-06-04T21:50:33.804654304Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/models/base_auth_model.py", line 24, in load_from_session 2021-06-04T21:50:33.804661504Z return session.load(model_cls=cls) 2021-06-04T21:50:33.804668404Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/core/session_manager.py", line 79, in load 2021-06-04T21:50:33.804675604Z session: StrsDict = self._read_session() 2021-06-04T21:50:33.804682604Z File "/usr/local/lib/python3.9/site-packages/fastapi_msal/core/session_manager.py", line 56, in _read_session 2021-06-04T21:50:33.804689804Z raise IOError( 2021-06-04T21:50:33.804696504Z OSError: No session id, (Make sure you initialized the session by calling init_session)

    My routes are protected with Depends(msal_auth.scheme).

    opened by jlerman44 10
  • Traceback when launching app contains error with itsdangerous

    Traceback when launching app contains error with itsdangerous

    I followed the instructions pretty carefully, but when launching the app and visiting the site I get

    kithara_base_1 | ERROR: Exception in ASGI application kithara_base_1 | Traceback (most recent call last): kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 371, in run_asgi kithara_base_1 | result = await app(self.scope, self.receive, self.send) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 59, in call kithara_base_1 | return await self.app(scope, receive, send) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 199, in call kithara_base_1 | await super().call(scope, receive, send) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 111, in call kithara_base_1 | await self.middleware_stack(scope, receive, send) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in call kithara_base_1 | raise exc from None kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in call kithara_base_1 | await self.app(scope, receive, _send) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/starlette/middleware/sessions.py", line 42, in call kithara_base_1 | data = self.signer.unsign(data, max_age=self.max_age) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/itsdangerous/timed.py", line 110, in unsign kithara_base_1 | raise sig_error kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/itsdangerous/timed.py", line 95, in unsign kithara_base_1 | result = super().unsign(signed_value) kithara_base_1 | File "/usr/local/lib/python3.9/site-packages/itsdangerous/signer.py", line 240, in unsign kithara_base_1 | raise BadSignature(f"No {self.sep!r} found in value") kithara_base_1 | itsdangerous.exc.BadSignature: No b'.' found in value

    opened by jlerman44 8
  • Question: Any way to hide the /docs that is compatible with fastapi_msal?

    Question: Any way to hide the /docs that is compatible with fastapi_msal?

    Hello dudil,

    I am trying to hide my documentation (so /docs, /redoc, and /openapi.json not accessible without msal login). Any idea how to achieve this?

    I have tried a few things based on reading:

    @app.get("/openapi.json", include_in_schema=False)
    async def get_open_api_endpoint(current_user: str = Depends(get_current_user)):
        return JSONResponse(get_openapi(title="Kithara", version=0.1, routes=app.routes))
    
    @app.get("/docs", include_in_schema=False)
    async def get_documentation(current_user: str = Depends(get_current_user)):
        return get_swagger_ui_html(openapi_url="/openapi.json", title="docs")
    

    and then initialize the fastapi app with

    app = FastAPI(docs_url=None, redoc_url=None, openapi_url=None)
    

    However, if this is done I do not think fastapi_msal can function. I get weird redirect related errors

    In this example "get_current_user" was just using some simple APIKeyCookie stuff.

    cookie_sec = APIKeyCookie(name="session")
    secret_key = "zzzzzzzzzzzzzzzzzzzzzzz"
    users = {"xxxxxxxxxxxxx": {"password": "yyyyyyyyyyyyyyyyyyyyy"}}
    
    # for login to docs
    def get_current_user(session: str = Depends(cookie_sec)):
        try:
            payload = jwt.decode(session, secret_key)
            user = users[payload["sub"]]
            return user
        except Exception:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN, detail="Invalid authentication"
            )
    

    Though ideally this would be Microsoft based login.

    Any ideas how I can achieve this?

    Stale 
    opened by jlerman44 7
  • fix RuntimeErrors from msal package

    fix RuntimeErrors from msal package

    MSALAuthCodeHandler.parse_id_token rewritten into different functions.

    AsyncConfClient.validate_id_token raised uncaught RuntimeError from the msal package whenever a invalid token was presented.

    Rearanged some things so there is a None for token_claims returned, as expected, instead of the the exception

    Stale 
    opened by yoerankaniok 4
  • Question: Cannot install fastapi_msal on Windows

    Question: Cannot install fastapi_msal on Windows

    I'm getting the following error messages when I try to pip install fastapi_msal:

    ERROR: Could not find a version that satisfies the requirement fastapi_msal (from versions: none) ERROR: No matching distribution found for fastapi_msal

    opened by Najib-Hoseini 4
  • Question: How to bypass MSAL authentication for unit testing?

    Question: How to bypass MSAL authentication for unit testing?

    Are there any best practices to bypass MSAL authentication for unit testing?

    I already tried to do so using dependency_overrides but I could not figure out how to make it work.

    Greetings, Michael

    Stale 
    opened by esenseial 4
  • Logout with fastapi_msal

    Logout with fastapi_msal

    Hi Dudil,

    Thanks a lot for your previous help on Python 3.9 --> 3.7. Everything works very smoothly!

    I am just facing an issue with the logout. I've tried to clear cookies via starlette's Response but it's not working. I was wondering whether fastapi_msal provides such functionality, either already or in the (near) future? Or maybe even how to handle expiration would be helpful :)

    Stale 
    opened by Najib-Hoseini 2
  • How do I get past this stage to get the access token?

    How do I get past this stage to get the access token?

    I'm very new to FastAPI and Python. It'd be great if I get some help here. I'm using Python 3.9. and I'm stuck at this this page. image

    I'm not sure how to change the redirect URI in the code.

    And also can you please let me know how we can protect APIs using the access token generated using MSAL and also validate the access token? cuz I tried to access the API by changing the access token but still it worked. So I wanted to know how I can validate the access token before executing it. Thanks in advance.

    opened by krakshayrao 2
  • Dependency overrides in unit testing

    Dependency overrides in unit testing

    First of all, thank you so much for making this great integration. You make it so easy to use AzureAD with FastAPI!

    And there is a stale discussion about this topic here: https://github.com/dudil/fastapi_msal/issues/4

    Since it's already closed, I thought I would create a new issue with more details because I think I'm encountering the same issue.

    So, I'd like to write unit testing for the /user/me endpoint. But since it has a dependency on the msal_auth.scheme, I need to bypass the authentication process.

    msal_client_config: MSALClientConfig = MSALClientConfig()
    ...
    
    msal_auth = MSALAuthorization(client_config=msal_client_config, return_to_path=settings.base_url)
    
    msal_auth_user = Depends(msal_auth.scheme)
    
    
    @router.get('/me', response_model=UserInfo, response_model_exclude_none=True, response_model_by_alias=False)
    async def get_user_me(current_user: UserInfo = msal_auth_user) -> UserInfo:
        return current_user
    

    I've tried to follow the tutorial about overriding dependencies from the FastAPI tutorial here. But it seems to be not working as the current_user still returns {'detail': 'Not authenticated'}

    This is how I override the dependencies

    def override_msal_scheme() -> UserInfo:
        return UserInfo(
            first_name='Test',
            last_name='User',
            display_name='Test User',
            emails=['[email protected]'],
            user_id='test_user_id',
        )
    
    
    @pytest.fixture(scope="module")
    def client() -> Generator:
        with TestClient(app) as c:
            app.dependency_overrides[msal_auth.scheme] = override_msal_scheme
            yield c
    

    If you can provide an example of how to bypass, silently override, or bypass the authentication process, that would be really helpful!

    opened by u-iandono 1
  • Role-based access control (RBAC) authorization

    Role-based access control (RBAC) authorization

    Adding authorization based on Role claims in the access_token

    This pull request aims to add the authorization part which is based on the access_token rather than the id_token. From the Microsoft documentation : "ID tokens should not be used for authorization purposes. Access tokens are used for authorization"

    This first change (using access_token instead of id_token) implies the client application (Swagger UI, CLI, Web app...) to request an access_token with a valid scope (how to expose your API - add a scope). The API then can perform the requested operation only if the access token it receives contains the scopes required.

    First level of authZ is to grant or deny access based on whether the entity making a request has been authenticated: Depends(msal_auth.scheme())

    Second level of authZ is to grant access to specific roles using the roles claims in the access_token: Depends(msal_auth.scheme(required_role = "specific_role"))

    Thanks in advance for having a look at this PR.

    opened by fbelhadi 0
  • Session backends (+file cache)

    Session backends (+file cache)

    This PR adds support for a persistent session cache via aiofiles. To use, install aiofiles pip install aiofiles and configure MSALClientConfig.

    from fastapi_msal.core import MSALClientConfig
    
    config = MSALClientConfig(
        session_type="filesystem",
        session_file_path="path/to/sessions",
    )
    msal_auth = MSALAuthorization(config)
    

    If using a config file like .env:

    SESSION_TYPE=filesystem
    SESSION_FILE_PATH=/path/to/sessions
    
    from fastapi_msal import MSALClientConfig
    
    settings = MSALClientConfig(_env_file=".env")
    
    opened by killjoy1221 0
  • Role-based access control (RBAC) authorization

    Role-based access control (RBAC) authorization

    Adding authorization based on Role claims in the access_token

    This pull request aims to add the authorization part which is based on the access_token rather than the id_token. From the Microsoft documentation : "ID tokens should not be used for authorization purposes. Access tokens are used for authorization"

    This first change (using access_token instead of id_token) implies the client application (Swagger UI, CLI, Web app...) to request an access_token with a valid scope (how to expose your API - add a scope). The API then can perform the requested operation only if the access token it receives contains the scopes required.

    First level of authZ is to grant or deny access based on whether the entity making a request has been authenticated: Depends(msal_auth.scheme())

    Second level of authZ is to grant access to specific roles using the roles claims in the access_token: Depends(msal_auth.scheme(required_role = "specific_role"))

    Thanks in advance for having a look at this PR.

    opened by fbelhadi 0
  • Exception in ASGI Application When Running Sample from README

    Exception in ASGI Application When Running Sample from README

    Describe the bug I'm get the following error:

    ERROR:    Exception in ASGI application
    Traceback (most recent call last):
      File "/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 375, in run_asgi
        result = await app(self.scope, self.receive, self.send)
      File "/usr/local/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
        return await self.app(scope, receive, send)
      File "/usr/local/lib/python3.9/site-packages/fastapi/applications.py", line 212, in __call__
        await super().__call__(scope, receive, send)
      File "/usr/local/lib/python3.9/site-packages/starlette/applications.py", line 112, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 181, in __call__
        raise exc
      File "/usr/local/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in __call__
        await self.app(scope, receive, _send)
      File "/usr/local/lib/python3.9/site-packages/starlette/middleware/sessions.py", line 77, in __call__
        await self.app(scope, receive, send_wrapper)
      File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
        raise exc
      File "/usr/local/lib/python3.9/site-packages/starlette/exceptions.py", line 71, in __call__
        await self.app(scope, receive, sender)
      File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 656, in __call__
        await route.handle(scope, receive, send)
      File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 259, in handle
        await self.app(scope, receive, send)
      File "/usr/local/lib/python3.9/site-packages/starlette/routing.py", line 61, in app
        response = await func(request)
      File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 226, in app
        raw_response = await run_endpoint_function(
      File "/usr/local/lib/python3.9/site-packages/fastapi/routing.py", line 159, in run_endpoint_function
        return await dependant.call(**values)
      File "/usr/local/lib/python3.9/site-packages/fastapi_msal/auth.py", line 83, in _post_token_route
        token: AuthToken = await self.handler.authorize_access_token(
      File "/usr/local/lib/python3.9/site-packages/fastapi_msal/security/msal_auth_code_handler.py", line 51, in authorize_access_token
        auth_token: AuthToken = await self.msal_app(cache=cache).finalize_auth_flow(
      File "/usr/local/lib/python3.9/site-packages/fastapi_msal/clients/async_conf_client.py", line 106, in finalize_auth_flow
        return AuthToken.parse_obj_debug(to_parse=auth_token)
      File "/usr/local/lib/python3.9/site-packages/fastapi_msal/models/base_auth_model.py", line 13, in parse_obj_debug
        debug_model: AuthModel = cls.parse_obj(obj=to_parse)
      File "pydantic/main.py", line 511, in pydantic.main.BaseModel.parse_obj
      File "pydantic/main.py", line 331, in pydantic.main.BaseModel.__init__
    pydantic.error_wrappers.ValidationError: 1 validation error for AuthToken
    id_token
      field required (type=value_error.missing)
    

    To Reproduce Steps to reproduce the behavior:

    1. Copied the code from the README
    2. Changed the client_id, client_credential, tenant and secret_key
    3. Run the code
    4. Goto http://localhost:5000/docs
    5. Click the lock on the left of GET /users/me
    6. In the popup, click Authorize
    7. auth errorError: Internal Server Error is displayed in the popup and the above stack trace is in the console

    Expected behavior Would expect to get the confirmation popup as mentioned in the README on step 6.

    Environment Settings

    • OS: Ubuntu 20.04 (WSL2)
    • Python Version: 3.9.5
    • Packages Versions: [msal 1.17.0 / fastapi 0.73.0 / fastapi_msal 0.1.7]

    Additional context Tried changing my secret_key as mentioned in https://github.com/dudil/fastapi_msal/issues/1 but didn't change anything.

    opened by LucO77 5
  • Expired token return Internal Server Error

    Expired token return Internal Server Error

    Describe the bug When you put an already expired token, it will return an internal server error.

    To Reproduce Steps to reproduce the behavior:

    1. Go to FastAPI autogenerated documentation /docs
    2. Wait until the token expired
    3. Try out a request

    Expected behavior It should return 401 Unauthorized instead of internal server error

    Environment Settings

    • OS: Windows
    • Python Version: 3.9
    • Packages Versions: [masl==1.16.0 / fastapi==0.73.0/ fastapi_msal==0.1.7]

    Additional context Error trace:

    Traceback (most recent call last):
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\uvicorn\protocols\http\httptools_impl.py", line 376, in run_asgi  
        result = await app(self.scope, self.receive, self.send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\uvicorn\middleware\proxy_headers.py", line 75, in __call__        
        return await self.app(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi\applications.py", line 212, in __call__
        await super().__call__(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\applications.py", line 112, in __call__
        await self.middleware_stack(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\middleware\errors.py", line 181, in __call__
        raise exc
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\middleware\errors.py", line 159, in __call__
        await self.app(scope, receive, _send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\middleware\cors.py", line 84, in __call__
        await self.app(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\middleware\sessions.py", line 77, in __call__
        await self.app(scope, receive, send_wrapper)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\exceptions.py", line 82, in __call__
        raise exc
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\exceptions.py", line 71, in __call__
        await self.app(scope, receive, sender)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\routing.py", line 656, in __call__
        await route.handle(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\routing.py", line 259, in handle
        await self.app(scope, receive, send)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\routing.py", line 61, in app
        response = await func(request)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi\routing.py", line 216, in app
        solved_result = await solve_dependencies(
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi\dependencies\utils.py", line 498, in solve_dependencies   
        solved_result = await solve_dependencies(
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi\dependencies\utils.py", line 527, in solve_dependencies   
        solved = await call(**sub_values)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi_msal\security\msal_scheme.py", line 47, in __call__       
        token_claims: Optional[IDTokenClaims] = await self.handler.parse_id_token(
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi_msal\security\msal_auth_code_handler.py", line 79, in pars
    e_id_token
        return await self.msal_app().validate_id_token(id_token=id_token)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi_msal\clients\async_conf_client.py", line 50, in validate_i
    d_token
        token_claims: OptStrsDict = await self.__execute_async__(
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\fastapi_msal\clients\async_conf_client.py", line 37, in __execute_
    async__
        result: T = await run_in_threadpool(func, **kwargs)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\starlette\concurrency.py", line 39, in run_in_threadpool
        return await anyio.to_thread.run_sync(func, *args)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\anyio\to_thread.py", line 28, in run_sync
        return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\anyio\_backends\_asyncio.py", line 818, in run_sync_in_worker_thre
    ad
        return await future
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\anyio\_backends\_asyncio.py", line 754, in run
        result = context.run(func, *args)
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\msal\oauth2cli\oidc.py", line 107, in decode_id_token
        return decode_id_token(
      File "C:\Users\x\.virtualenvs\y-M0aUaMTg\lib\site-packages\msal\oauth2cli\oidc.py", line 76, in decode_id_token
        raise RuntimeError("%s Current epoch = %s.  The id_token was: %s" % (
    RuntimeError: 9. The current time MUST be before the time represented by the exp Claim. Current epoch = 1644427758.  The id_token was: {
      "aud": "7907897b-f451-4056-a529-xxxxxxxxxxxx",
      "iss": "https://login.microsoftonline.com/94118b0c-61a0-42a1-99c9-xxxxxxxxxxxx/v2.0",
      "iat": 1644417071,
      "nbf": 1644417071,
      "exp": 1644420971,
      "idp": "https://sts.windows.net/9188040d-6c67-4c5b-b112-xxxxxxxxxxxx/",
      "name": "111 dbss",
      "nonce": "edfe0744ff8456867b3c7a0bf3c252a7e053b33e97cbf5dd9012fe9b3944376f",
      "oid": "c47f7dbb-ecd3-475c-ab5b-xxxxxxxxxxxx",
      "preferred_username": "[email protected]",
      "rh": "0.AXEADIsRlKBhoUKZyeU0YCG2pnuJB3lR9FZApSly7PrGSQ-HALI.",
      "sub": "j-eDpYyo8Dbr8cNwdiQwXzl_xxxxxxxxxxxxxxxxx",
      "tid": "94118b0c-61a0-42a1-99c9-xxxxxxxxxxxx",
      "uti": "vXXE-Eq6PE-xxxxxxxxxxx",
      "ver": "2.0"
    }
    
    opened by u-iandono 0
  • How to get group claims?

    How to get group claims?

    I'm trying to get group information into my FastAPI app from MSAL, but I can't see that it's supported.

    Describe the solution you'd like I want to add group claims in my token configuration and have them show up in the UserInfo model for use in authorizations in my endpoints.

    Describe alternatives you've considered I've configured my token configuration in my AZ AD App to "emit groups as role claims" for ID and Access types.

    Additional context None.

    opened by philipsd6 3
  • Document how to get a token for accessing the API via curl

    Document how to get a token for accessing the API via curl

    Is your feature request related to a problem? Please describe. The authentication flow from the Swagger Docs UI is clear, but what isn't discussed is how a user could use the API directly from curl, without reusing the token retrieved from the Swagger Docs authentication flow.

    Describe the solution you'd like A session-less/Swagger Doc-less way to use an API I protect with msal_auth.scheme. For example:

    curl http://localhost:8000/users/me  # 401 Unauthorized: {"detail":"Not authenticated"}
    

    So we need to login:

    curl -v http://localhost:8000/login  # 307 Temporary Redirect
    # location: https://login.microsoftonline.com/.../oauth2/v2.0/authorize?...&redirect_uri=http%3A%2F%2Flocalhost%3A8000%2Ftoken
    

    So I ensure http://localhost:8000/token is added to my app registrations allowed redirect URIs. Then I open the location URI in my browser and go through the Azure authentication flow in the portal, just as with the Swagger Docs UI auth flow. It should redirect to my /token endpoint which will return a token for me to use later:

    curl -H @auth.header.txt http://localhost:8000/users/me
    

    Instead I get {"detail":"Authentication Error"} from my local API.

    Describe alternatives you've considered I've tried to look into service principals, and adding an App Role "Can Invoke my API" but I cannot actually set those up without tenant admin access. I should be able to accomplish my goals without requiring Admin access.

    Additional context Going a completely different direction, ideally, we should be able to protect our API with MSAL, but then internally once authenticated, be able to generate granular tokens for use via Curl, etc... i.e. a user might have admin access to the API via roles returned from MSAL, but then once logged into the local API, they should be able to generate a less-privileged token for use in their scripts. I have no idea how one might set that up though.

    opened by philipsd6 3
Releases(1.7)
  • 1.7(Aug 11, 2021)

    1. Updated to support python version 3.7 and above (was 3.9 and above)
    2. Fix session not initialize bug on first login
    3. Updated readme with working example
    Source code(tar.gz)
    Source code(zip)
Owner
Dudi Levy
I am Technology executive leading people, products and technology. I love to use technology as enabler for great ideas and products people love to use.
Dudi Levy
Restful Api developed with Flask using Prometheus and Grafana for monitoring and containerization with Docker :rocket:

Hephaestus 🚀 In Greek mythology, Hephaestus was either the son of Zeus and Hera or he was Hera's parthenogenous child. ... As a smithing god, Hephaes

Yasser Tahiri 16 Oct 07, 2022
Starlette middleware for Prerender

Prerender Python Starlette Starlette middleware for Prerender Documentation: https://BeeMyDesk.github.io/prerender-python-starlette/ Source Code: http

BeeMyDesk 14 May 02, 2021
Github timeline htmx based web app rewritten from Common Lisp to Python FastAPI

python-fastapi-github-timeline Rewrite of Common Lisp htmx app _cl-github-timeline into Python using FastAPI. This project tries to prove, that with h

Jan Vlčinský 4 Mar 25, 2022
FastAPI Project Template

The base to start an openapi project featuring: SQLModel, Typer, FastAPI, JWT Token Auth, Interactive Shell, Management Commands.

A.Freud 4 Dec 05, 2022
Adds simple SQLAlchemy support to FastAPI

FastAPI-SQLAlchemy FastAPI-SQLAlchemy provides a simple integration between FastAPI and SQLAlchemy in your application. It gives access to useful help

Michael Freeborn 465 Jan 07, 2023
A FastAPI Plug-In to support authentication authorization using the Microsoft Authentication Library (MSAL)

FastAPI/MSAL - MSAL (Microsoft Authentication Library) plugin for FastAPI FastAPI - https://github.com/tiangolo/fastapi FastAPI is a modern, fast (hig

Dudi Levy 15 Jul 20, 2022
Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚡

csrf-starlette-fastapi Dead simple CSRF security middleware for Starlette ⭐ and Fast API ⚡ Will work with either a input type="hidden" field or ajax

Nathaniel Sabanski 9 Nov 20, 2022
A fast and durable Pub/Sub channel over Websockets. FastAPI + WebSockets + PubSub == ⚡ 💪 ❤️

⚡ 🗞️ FastAPI Websocket Pub/Sub A fast and durable Pub/Sub channel over Websockets. The easiest way to create a live publish / subscribe multi-cast ov

8 Dec 06, 2022
Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automatically use request headers such as x-request-id or x-correlation-id.

starlette context Middleware for Starlette that allows you to store and access the context data of a request. Can be used with logging so logs automat

Tomasz Wójcik 300 Dec 26, 2022
This is an API developed in python with the FastApi framework and putting into practice the recommendations of the book Clean Architecture in Python by Leonardo Giordani,

This is an API developed in python with the FastApi framework and putting into practice the recommendations of the book Clean Architecture in Python by Leonardo Giordani,

0 Sep 24, 2022
High-performance Async REST API, in Python. FastAPI + GINO + Arq + Uvicorn (w/ Redis and PostgreSQL).

fastapi-gino-arq-uvicorn High-performance Async REST API, in Python. FastAPI + GINO + Arq + Uvicorn (powered by Redis & PostgreSQL). Contents Get Star

Leo Sussan 351 Jan 04, 2023
Simple web app example serving a PyTorch model using streamlit and FastAPI

streamlit-fastapi-model-serving Simple example of usage of streamlit and FastAPI for ML model serving described on this blogpost and PyConES 2020 vide

Davide Fiocco 291 Jan 06, 2023
FastAPI Auth Starter Project

This is a template for FastAPI that comes with authentication preconfigured.

Oluwaseyifunmi Oyefeso 6 Nov 13, 2022
Keycloak integration for Python FastAPI

FastAPI Keycloak Integration Documentation Introduction Welcome to fastapi-keycloak. This projects goal is to ease the integration of Keycloak (OpenID

Code Specialist 113 Dec 31, 2022
Adds GraphQL support to your Flask application.

Flask-GraphQL Adds GraphQL support to your Flask application. Usage Just use the GraphQLView view from flask_graphql from flask import Flask from flas

GraphQL Python 1.3k Dec 31, 2022
Generate Class & Decorators for your FastAPI project ✨🚀

Classes and Decorators to use FastAPI with class based routing. In particular this allows you to construct an instance of a class and have methods of that instance be route handlers for FastAPI & Pyt

Yasser Tahiri 34 Oct 27, 2022
Farlimit - FastAPI rate limit with python

FastAPIRateLimit Contributing is F&E (free&easy) Y Usage pip install farlimit N

omid 27 Oct 06, 2022
Toolkit for developing and maintaining ML models

modelkit Python framework for production ML systems. modelkit is a minimalist yet powerful MLOps library for Python, built for people who want to depl

140 Dec 27, 2022
🚢 Docker images and utilities to power your Python APIs and help you ship faster. With support for Uvicorn, Gunicorn, Starlette, and FastAPI.

🚢 inboard 🐳 Docker images and utilities to power your Python APIs and help you ship faster. Description This repository provides Docker images and a

Brendon Smith 112 Dec 30, 2022
A FastAPI Middleware of joerick/pyinstrument to check your service performance.

fastapi_profiler A FastAPI Middleware of joerick/pyinstrument to check your service performance. 📣 Info A FastAPI Middleware of pyinstrument to check

LeoSun 107 Jan 05, 2023