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
Deploy/View images to database sqlite with fastapi

Deploy/View images to database sqlite with fastapi cd realistic Dependencies dat

Fredh Macau 1 Jan 04, 2022
implementation of deta base for FastAPIUsers

FastAPI Users - Database adapter for Deta Base Ready-to-use and customizable users management for FastAPI Documentation: https://fastapi-users.github.

2 Aug 15, 2022
API written using Fast API to manage events and implement a leaderboard / badge system.

Open Food Facts Events API written using Fast API to manage events and implement a leaderboard / badge system. Installation To run the API locally, ru

Open Food Facts 5 Jan 07, 2023
FastAPI CRUD template using Deta Base

Deta Base FastAPI CRUD FastAPI CRUD template using Deta Base Setup Install the requirements for the CRUD: pip3 install -r requirements.txt Add your D

Sebastian Ponce 2 Dec 15, 2021
Regex Converter for Flask URL Routes

Flask-Reggie Enable Regex Routes within Flask Installation pip install flask-reggie Configuration To enable regex routes within your application from

Rhys Elsmore 48 Mar 07, 2022
Prometheus exporter for several chia node statistics

prometheus-chia-exporter Prometheus exporter for several chia node statistics It's assumed that the full node, the harvester and the wallet run on the

30 Sep 19, 2022
Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application.

Flask-Bcrypt Flask-Bcrypt is a Flask extension that provides bcrypt hashing utilities for your application. Due to the recent increased prevelance of

Max Countryman 310 Dec 14, 2022
Fast, simple API for Apple firmwares.

Loyal Fast, Simple API for fetching Apple Firmwares. The API server is closed due to some reasons. Wait for v2 releases. Features Fetching Signed IPSW

11 Oct 28, 2022
A comprehensive CRUD API generator for SQLALchemy.

FastAPI Quick CRUD Introduction Advantage Constraint Getting started Installation Usage Design Path Parameter Query Parameter Request Body Upsert Intr

192 Jan 06, 2023
Cookiecutter API for creating Custom Skills for Azure Search using Python and Docker

cookiecutter-spacy-fastapi Python cookiecutter API for quick deployments of spaCy models with FastAPI Azure Search The API interface is compatible wit

Microsoft 379 Jan 03, 2023
Redis-based rate-limiting for FastAPI

Redis-based rate-limiting for FastAPI

Glib 6 Nov 14, 2022
API Simples com python utilizando a biblioteca FastApi

api-fastapi-python API Simples com python utilizando a biblioteca FastApi Para rodar esse script são necessárias duas bibliotecas: Fastapi: Comando de

Leonardo Grava 0 Apr 29, 2022
Keepalive - Discord Bot to keep threads from expiring

keepalive Discord Bot to keep threads from expiring Installation Create a new Di

Francesco Pierfederici 5 Mar 14, 2022
Instrument your FastAPI app

Prometheus FastAPI Instrumentator A configurable and modular Prometheus Instrumentator for your FastAPI. Install prometheus-fastapi-instrumentator fro

Tim Schwenke 441 Jan 05, 2023
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
FastAPI + PeeWee = <3

FastAPIwee FastAPI + PeeWee = 3 Using Python = 3.6 🐍 Installation pip install FastAPIwee 🎉 Documentation Documentation can be found here: https://

16 Aug 30, 2022
FastAPI with Docker and Traefik

Dockerizing FastAPI with Postgres, Uvicorn, and Traefik Want to learn how to build this? Check out the post. Want to use this project? Development Bui

51 Jan 06, 2023
Opinionated authorization package for FastAPI

FastAPI Authorization Installation pip install fastapi-authorization Usage Currently, there are two models available: RBAC: Role-based Access Control

Marcelo Trylesinski 18 Jul 04, 2022
Fastapi performans monitoring

Fastapi-performans-monitoring This project is a simple performance monitoring for FastAPI. License This project is licensed under the terms of the MIT

bilal alpaslan 11 Dec 31, 2022
User authentication fastapi with python

user-authentication-fastapi Authentication API Development Setup environment You should create a virtual environment and activate it: virtualenv venv

Sabir Hussain 3 Mar 03, 2022