Easy and secure implementation of Azure AD for your FastAPI APIs πŸ”’ Single- and multi-tenant support.

Overview


FastAPI-Azure-Auth

Azure AD Authentication for FastAPI apps made easy.

Python version FastAPI Version Package version
Codecov Pre-commit Black mypy isort

πŸš€ Description

FastAPI is a modern, fast (high-performance), web framework for building APIs with Python, based on standard Python type hints.

At Intility we use FastAPI for both internal (single-tenant) and customer-facing (multi-tenant) APIs. This package enables our developers (and you 😊 ) to create features without worrying about authentication and authorization.

Also, we're hiring!

πŸ“š Resources

The documentation contains a full tutorial on how to configure Azure AD and FastAPI for both single- and multi-tenant applications. It includes examples on how to lock down your APIs to certain scopes, tenants, roles etc. For first time users it's strongly advised to set up your application exactly how it's described there, and then alter it to your needs later.

MIT License | Documentation | GitHub

⚑ Setup

This is a tl;dr intended to give you an idea of what this package does and how to use it. For a more in-depth tutorial and settings reference you should read the documentation.

1. Install this library:

pip install fastapi-azure-auth
# or
poetry add fastapi-azure-auth

2. Configure your FastAPI app

Include swagger_ui_oauth2_redirect_url and swagger_ui_init_oauth in your FastAPI app initialization:

# file: main.py
app = FastAPI(
    ...
    swagger_ui_oauth2_redirect_url='/oauth2-redirect',
    swagger_ui_init_oauth={
        'usePkceWithAuthorizationCodeGrant': True,
        'clientId': settings.OPENAPI_CLIENT_ID,
    },
)

3. Setup CORS

Ensure you have CORS enabled for your local environment, such as http://localhost:8000.

4. Configure FastAPI-Azure-Auth

Configure either your SingleTenantAzureAuthorizationCodeBearer or MultiTenantAzureAuthorizationCodeBearer.

# file: demoproj/api/dependencies.py
from fastapi_azure_auth.auth import SingleTenantAzureAuthorizationCodeBearer

azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
    app_client_id=settings.APP_CLIENT_ID,
    tenant_id=settings.TENANT_ID,
    scopes={
        f'api://{settings.APP_CLIENT_ID}/user_impersonation': 'user_impersonation',
    }
)

or for multi-tenant applications:

# file: demoproj/api/dependencies.py
from fastapi_azure_auth.auth import MultiTenantAzureAuthorizationCodeBearer

azure_scheme = MultiTenantAzureAuthorizationCodeBearer(
    app_client_id=settings.APP_CLIENT_ID,
    scopes={
        f'api://{settings.APP_CLIENT_ID}/user_impersonation': 'user_impersonation',
    },    
    validate_iss=False
)

To validate the iss, configure an iss_callable.

5. Configure dependencies

Add azure_scheme as a dependency for your views/routers, using either Security() or Depends().

# file: main.py
from demoproj.api.dependencies import azure_scheme

app.include_router(api_router, prefix=settings.API_V1_STR, dependencies=[Security(azure_scheme, scopes='user_impersonation')])

6. Load config on startup

Optional but recommended.

# file: main.py
@app.on_event('startup')
async def load_config() -> None:
    """
    Load OpenID config on startup.
    """
    await azure_scheme.openid_config.load_config()

πŸ“„ Example OpenAPI documentation

Your OpenAPI documentation will get an Authorize button, which can be used to authenticate. authorize

The user can select which scopes to authenticate with, based on your configuration. scopes

Comments
  • Feature/b2c support

    Feature/b2c support

    Added support for tokens without x5c fields, and added optional openid_config_url override, this would close #46

    Not all tests are working yet, not sure if that is due to the code itself or due to the test config

    opened by robteeuwen 18
  • B2C token does not contain `tid` in the token

    B2C token does not contain `tid` in the token

    I've managed to set up B2C, but it doesn't seem to return tid in the token. Everything else is working.

    I'm not sure how to confirm if everything is working well (I might have to contact microsoft), but if B2C doesn't return tid, would it make sense to make it optional in the User model?

    question 
    opened by marcinplatek 17
  • [BUG] TypeError at library import (Python 3.9.1)

    [BUG] TypeError at library import (Python 3.9.1)

    Describe the bug I'm new to Python/FastAPI, so it is very well possible that it's just me doing something wrong, but my code fails to compile as soon as I add the line to import either SingleTenantAzureAuthorizationCodeBearer or MultiTenantAzureAuthorizationCodeBearer and I'm getting this error: TypeError: unhashable type: 'list'

    First I thought it's related to my already written code, so I've started a clean project and followed the tutorial in the documentation, but that also fails as soon as I add the import line. So I'm completely lost here.

    I'm using Python 3.9.1 and FastAPI 0.74.1

    To Reproduce Add code line: from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer

    Stack trace

    Traceback (most recent call last):
      File "c:\program files\python39\lib\runpy.py", line 197, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "c:\program files\python39\lib\runpy.py", line 87, in _run_code
        exec(code, run_globals)
      File "C:\Users\...\AppData\Roaming\Python\Python39\Scripts\uvicorn.exe\__main__.py", line 7, in <module>
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\click\core.py", line 1137, in __call__
        return self.main(*args, **kwargs)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\click\core.py", line 1062, in main
        rv = self.invoke(ctx)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\click\core.py", line 1404, in invoke
        return ctx.invoke(self.callback, **ctx.params)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\click\core.py", line 763, in invoke
        return __callback(*args, **kwargs)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\main.py", line 435, in main
        run(app, **kwargs)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\main.py", line 461, in run
        server.run()
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\server.py", line 60, in run
        return asyncio.run(self.serve(sockets=sockets))
      File "c:\program files\python39\lib\asyncio\runners.py", line 44, in run
        return loop.run_until_complete(main)
      File "c:\program files\python39\lib\asyncio\base_events.py", line 642, in run_until_complete
        return future.result()
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\server.py", line 67, in serve
        config.load()
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\config.py", line 458, in load
        self.loaded_app = import_from_string(self.app)
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\uvicorn\importer.py", line 21, in import_from_string
        module = importlib.import_module(module_str)
      File "c:\program files\python39\lib\importlib\__init__.py", line 127, in import_module
        return _bootstrap._gcd_import(name[level:], package, level)
      File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
      File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
      File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
      File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
      File "<frozen importlib._bootstrap_external>", line 790, in exec_module
      File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
      File ".\main.py", line 7, in <module>
        from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\fastapi_azure_auth\__init__.py", line 1, in <module>
        from fastapi_azure_auth.auth import (  # noqa: F401
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\fastapi_azure_auth\auth.py", line 20, in <module>
        class AzureAuthorizationCodeBearerBase(SecurityBase):
      File "C:\Users\...\AppData\Roaming\Python\Python39\site-packages\fastapi_azure_auth\auth.py", line 29, in AzureAuthorizationCodeBearerBase
        iss_callable: Optional[Callable[[str], Awaitable[str]]] = None,
      File "c:\program files\python39\lib\typing.py", line 262, in inner
        return func(*args, **kwds)
      File "c:\program files\python39\lib\typing.py", line 339, in __getitem__
        return self._getitem(self, parameters)
      File "c:\program files\python39\lib\typing.py", line 463, in Optional
        return Union[arg, type(None)]
      File "c:\program files\python39\lib\typing.py", line 262, in inner
        return func(*args, **kwds)
      File "c:\program files\python39\lib\typing.py", line 339, in __getitem__
        return self._getitem(self, parameters)
      File "c:\program files\python39\lib\typing.py", line 451, in Union
        parameters = _remove_dups_flatten(parameters)
      File "c:\program files\python39\lib\typing.py", line 231, in _remove_dups_flatten
        return tuple(_deduplicate(params))
      File "c:\program files\python39\lib\typing.py", line 205, in _deduplicate
        all_params = set(params)
    TypeError: unhashable type: 'list'
    

    Your configuration Exactly the same as the tutorial here: https://intility.github.io/fastapi-azure-auth/single-tenant/fastapi_configuration

    bug 
    opened by ravaszf 17
  • [BUG/Question] Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type.

    [BUG/Question] Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type.

    Describe the bug

    Auth error
    Error: Bad Request,
    error: invalid_request,
    description: AADSTS9002326: Cross-origin token redemption is permitted only for the 'Single-Page Application' client-type.
    

    To Reproduce

    This is the minimal FastAPI app:

    from pydantic import AnyHttpUrl, BaseSettings, Field
    from fastapi.middleware.cors import CORSMiddleware
    from typing import Union
    
    class Settings(BaseSettings):
        SECRET_KEY: str = Field('my super secret key', env='SECRET_KEY')
        BACKEND_CORS_ORIGINS: list[Union[str, AnyHttpUrl]] = ['http://localhost:8000']
        OPENAPI_CLIENT_ID: str = Field(default='', env='OPENAPI_CLIENT_ID')
        APP_CLIENT_ID: str = Field(default='', env='APP_CLIENT_ID')
        TENANT_ID: str = Field(default='', env='TENANT_ID')
    
        class Config:
            env_file = '.env'
            env_file_encoding = 'utf-8'
            case_sensitive = True
    
    from fastapi import FastAPI
    
    settings = Settings()
    app = FastAPI()
    
    settings = Settings()
    if settings.BACKEND_CORS_ORIGINS:
        app.add_middleware(
            CORSMiddleware,
            allow_origins=[str(origin) for origin in settings.BACKEND_CORS_ORIGINS],
            allow_credentials=True,
            allow_methods=['*'],
            allow_headers=['*'],
        )
    
    app = FastAPI(
        swagger_ui_oauth2_redirect_url='/oauth2-redirect',
        swagger_ui_init_oauth={
            'usePkceWithAuthorizationCodeGrant': True,
            'clientId': settings.OPENAPI_CLIENT_ID,
        })
    
    from fastapi_azure_auth import SingleTenantAzureAuthorizationCodeBearer
    
    azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
        app_client_id=settings.APP_CLIENT_ID,
        tenant_id=settings.TENANT_ID,
        scopes={
            #"User.ReadBasic.All": 'read'
            'https://graph.microsoft.com/.default': 'default'
            #AADSTS70011
            #f'api://{settings.APP_CLIENT_ID}/user_impersonation': 'user_impersonation',
        })
    
    @app.on_event('startup')
    async def load_config() -> None:
        """    Load OpenID config on startup.    """
        await azure_scheme.openid_config.load_config()
    
    from fastapi import Security, responses
    
    @app.get("/", dependencies=[Security(azure_scheme, scopes=["default"])])
    def read_root():
        """
        Redirects to /docs
        """
        return "It works."
    

    Please, set the following envars:

    export TENANT_ID=<your-tenant_id>
    export OPENAPI_CLIENT_ID=<your-client_id>
    export APP_CLIENT_ID="https://login.microsoftonline.com/$TENANT_ID"
    export SECRET_KEY=<your-secret>
    

    Steps to reproduce the behavior:

    1. Go to http://localhost:8000/docs
    2. Click in 'Autorize'
    3. Leave client_secret blank, and select scopes
    4. Click in 'Autorize', the page will return the error

    Configuration

    I believe this bug is related to my Azure AD set up, so may provide the Manifest from AD. Sensitive information is hidden and the <CENSORED> is put in place.

    {
    	"id": "<CENSORED>",
    	"acceptMappedClaims": null,
    	"accessTokenAcceptedVersion": 2,
    	"addIns": [],
    	"allowPublicClient": false,
    	"appId": "<CENSORED>",
    	"appRoles": [],
    	"oauth2AllowUrlPathMatching": false,
    	"createdDateTime": "2022-01-11T19:43:15Z",
    	"description": null,
    	"certification": null,
    	"disabledByMicrosoftStatus": null,
    	"groupMembershipClaims": null,
    	"identifierUris": [],
    	"informationalUrls": {
    		"termsOfService": null,
    		"support": null,
    		"privacy": null,
    		"marketing": null
    	},
    	"keyCredentials": [],
    	"knownClientApplications": [],
    	"logoUrl": null,
    	"logoutUrl": "https://localhost:8000/oauth2-redirect",
    	"name": "backoffice",
    	"notes": null,
    	"oauth2AllowIdTokenImplicitFlow": true,
    	"oauth2AllowImplicitFlow": true,
    	"oauth2Permissions": [],
    	"oauth2RequirePostResponse": false,
    	"optionalClaims": null,
    	"orgRestrictions": [],
    	"parentalControlSettings": {
    		"countriesBlockedForMinors": [],
    		"legalAgeGroupRule": "Allow"
    	},
    	"passwordCredentials": [
    		{
    			"customKeyIdentifier": null,
    			"endDate": "2022-04-21T17:02:20.006Z",
    			"keyId": "<CENSORED>",
    			"startDate": "2022-01-21T17:02:20.006Z",
    			"value": null,
    			"createdOn": "2022-01-21T17:02:31.8956842Z",
    			"hint": ".F7",
    			"displayName": "API-Test"
    		}
    	],
    	"preAuthorizedApplications": [],
    	"publisherDomain": "<CENSORED>",
    	"replyUrlsWithType": [
    		{
    			"url": "http://localhost:8000/",
    			"type": "Web"
    		},
    		{
    			"url": "http://localhost:8000/oauth2-redirect",
    			"type": "Web"
    		},
    	],
    	"requiredResourceAccess": [
    		{
    			"resourceAppId": "00000003-0000-0000-c000-000000000000",
    			"resourceAccess": [
    				{
    					"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d",
    					"type": "Scope"
    				},
    				{
    					"id": "14dad69e-099b-42c9-810b-d002981feec1",
    					"type": "Scope"
    				}
    			]
    		}
    	],
    	"samlMetadataUrl": null,
    	"serviceManagementReference": null,
    	"signInUrl": null,
    	"signInAudience": "AzureADMyOrg",
    	"tags": [],
    	"tokenEncryptionKeyId": null
    }
    
    question 
    opened by Vido 17
  • [Question] Login integration

    [Question] Login integration

    First, thank you for the library it's great!

    Would be nice to see a very basic integration between the library and a logging endpoint in order to understand the integration with the library in that process.

    I can logging with the Fastapi docs but not from my app. I feel I'm reinventing the wheel trying to do the authentication steps. I'm ending creating my own functions try to follow the oath2 flow.

    Thanks!

    question 
    opened by productoptimization 12
  • Update the _types module to primitives types

    Update the _types module to primitives types

    This is a change in the way the hazmat section of the cryptography module is organized. This fixes the error received when using the most up to date version of cryptography (at the date of writing).

    Close #25

    opened by bmoore 9
  • Support Python 3.8

    Support Python 3.8

    Following is the summary of changes done -

    1. Using type hints from typing library instead of built-in type hints introduced in python 3.9.

    2. Using older way to concatenate 2 dictionaries instead of newer one.

    Example of older way to concatenate 2 dictionaries -> dmerged = {**d1,**d2}, here d1 and d2 are 2 dictionaries, merged into 1 as dmerged

    Close #74

    opened by manupatel007 8
  • [Documentation]: Suggested update in walkthrough

    [Documentation]: Suggested update in walkthrough

    Describe the issue (https://intility.github.io/fastapi-azure-auth/single-tenant/azure_setup#step-4---allow-openapi-to-talk-to-the-backend)

    There should be one final step on this walk through. Azure has added another section under "Exposing an API" called "Authorized client applications". If you do not link the OpenAPI back to the original application, the end-user will be prompted to request consent from an administrator.

    To Reproduce Manually go through the Single Tenant walkthrough (https://intility.github.io/fastapi-azure-auth/single-tenant/). Once fully complete, any attempt to log into Azure via OpenAPI will send the end-user to a page, as described below:

    Selection_030

    Fix This is so the end-user doesn't see this prompt. 1.) Go to "Expose an API" under your Sample application (not the OpenAPI). 2.) Under "Authorized client applications" click "Add a client application"

    Selection_029

    3.) Copy the Application Id from your 'Sample - OpenAPI' into the 'Client ID' field. 4.) Select the exposed 'api://*' check box 5.) Click "Add Application"

    Now when you click "Authorize"/"Login" via SwaggerUI, the end user will no longer require Admin consent.

    I hope this helps, -Brian

    documentation good first issue 
    opened by bkmetzler 8
  • [BUG] AttributeError: partially initialized module 'anyio._backends._asyncio' has no attribute 'Event'

    [BUG] AttributeError: partially initialized module 'anyio._backends._asyncio' has no attribute 'Event'

    Describe the bug

    We are seeing this error reported in our sentry for a FastApi Azure function based on https://github.com/ecerami/fastapi_azure and fastapi-azure-auth

    partially initialized module 'anyio._backends._asyncio' has no attribute 'Event' (most likely due to a circular import)

    see https://github.com/Intility/fastapi-azure-auth/blob/main/fastapi_azure_auth/openid_config.py#L55

    To Reproduce

    Not sure how to reproduce, it only happens occasionally. Our app is still in early phase and does not see real traffic.

    Stack trace

    AttributeError: partially initialized module 'anyio._backends._asyncio' has no attribute 'Event' (most likely due to a circular import)
      File "fastapi_azure_auth/openid_config.py", line 79, in _load_openid_config
        openid_response = await client.get(config_url)
      File "httpx/_client.py", line 1729, in get
        return await self.request(
      File "httpx/_client.py", line 1506, in request
        return await self.send(request, auth=auth, follow_redirects=follow_redirects)
      File "httpx/_client.py", line 1593, in send
        response = await self._send_handling_auth(
      File "httpx/_client.py", line 1621, in _send_handling_auth
        response = await self._send_handling_redirects(
      File "httpx/_client.py", line 1658, in _send_handling_redirects
        response = await self._send_single_request(request)
      File "httpx/_client.py", line 1695, in _send_single_request
        response = await transport.handle_async_request(request)
      File "httpx/_transports/default.py", line 353, in handle_async_request
        resp = await self._pool.handle_async_request(req)
      File "httpcore/_async/connection_pool.py", line 216, in handle_async_request
        status = RequestStatus(request)
      File "httpcore/_async/connection_pool.py", line 19, in __init__
        self._connection_acquired = AsyncEvent()
      File "httpcore/_synchronization.py", line 29, in __init__
        self._event = anyio.Event()
      File "anyio/_core/_synchronization.py", line 77, in __new__
        return get_asynclib().Event()
    
    AttributeError: partially initialized module 'anyio._backends._asyncio' has no attribute 'checkpoint_if_cancelled' (most likely due to a circular import)
      File "fastapi_azure_auth/openid_config.py", line 42, in load_config
        await self._load_openid_config()
      File "fastapi_azure_auth/openid_config.py", line 91, in _load_openid_config
        self._load_keys(jwks_response.json()['keys'])
      File "httpx/_client.py", line 1975, in __aexit__
        await self._transport.__aexit__(exc_type, exc_value, traceback)
      File "httpx/_transports/default.py", line 332, in __aexit__
        await self._pool.__aexit__(exc_type, exc_value, traceback)
      File "httpcore/_async/connection_pool.py", line 326, in __aexit__
        await self.aclose()
      File "httpcore/_async/connection_pool.py", line 303, in aclose
        async with self._pool_lock:
      File "httpcore/_synchronization.py", line 15, in __aenter__
        await self._lock.acquire()
      File "anyio/_core/_synchronization.py", line 117, in acquire
        await checkpoint_if_cancelled()
      File "anyio/lowlevel.py", line 42, in checkpoint_if_cancelled
        await get_asynclib().checkpoint_if_cancelled()
    
    RuntimeError: Unable to fetch provider information. partially initialized module 'anyio._backends._asyncio' has no attribute 'checkpoint_if_cancelled' (most likely due to a circular import)
      File "starlette/exceptions.py", line 93, in __call__
        raise exc
      File "starlette/exceptions.py", line 82, in __call__
        await self.app(scope, receive, sender)
      File "fastapi/middleware/asyncexitstack.py", line 21, in __call__
        raise e
      File "fastapi/middleware/asyncexitstack.py", line 18, in __call__
        await self.app(scope, receive, send)
      File "starlette/routing.py", line 670, in __call__
        await route.handle(scope, receive, send)
      File "starlette/routing.py", line 418, in handle
        await self.app(scope, receive, send)
      File "fastapi/applications.py", line 269, in __call__
        await super().__call__(scope, receive, send)
      File "starlette/applications.py", line 124, in __call__
        await self.middleware_stack(scope, receive, send)
      File "starlette/middleware/errors.py", line 184, in __call__
        raise exc
      File "starlette/middleware/errors.py", line 162, in __call__
        await self.app(scope, receive, _send)
      File "starlette/exceptions.py", line 93, in __call__
        raise exc
      File "starlette/exceptions.py", line 82, in __call__
        await self.app(scope, receive, sender)
      File "fastapi/middleware/asyncexitstack.py", line 21, in __call__
        raise e
      File "fastapi/middleware/asyncexitstack.py", line 18, in __call__
        await self.app(scope, receive, send)
      File "starlette/routing.py", line 670, in __call__
        await route.handle(scope, receive, send)
      File "starlette/routing.py", line 266, in handle
        await self.app(scope, receive, send)
      File "starlette/routing.py", line 65, in app
        response = await func(request)
      File "fastapi/routing.py", line 217, in app
        solved_result = await solve_dependencies(
      File "fastapi/dependencies/utils.py", line 524, in solve_dependencies
        solved = await call(**sub_values)
      File "fastapi_azure_auth/auth.py", line 151, in __call__
        await self.openid_config.load_config()
      File "fastapi_azure_auth/openid_config.py", line 55, in load_config
        raise RuntimeError(f'Unable to fetch provider information. {error}') from error
    

    Your configuration

    class AuthSettings(BaseSettings):
        SECRET_KEY: str = Field("secret key", env="SECRET_KEY")
        BACKEND_CORS_ORIGINS: list[Union[str, AnyHttpUrl]] = [
            "http://localhost:7071",
            "http://localhost:8000",
            "https://foo.azurewebsites.net",
        ]
        OPENAPI_CLIENT_ID: str = Field(default="", env="OPENAPI_CLIENT_ID")
        APP_CLIENT_ID: str = Field(default="", env="APP_CLIENT_ID")
        TENANT_ID: str = Field(default="", env="TENANT_ID")
    
        class Config:
            env_file = "settings.env"
            env_file_encoding = "utf-8"
            case_sensitive = True
    
    
    auth_settings = AuthSettings()
    
    
    class FastAPISettings(BaseSettings):
        debug: bool = Field(default=False, env="DEBUG")
        title: str = "Foo"
        description: str = (
            "..."
        )
        version: str = "0.0.1"
        contact: dict = {
            "name": "Foo",
        }
        swagger_ui_oauth2_redirect_url: str = "/oauth2-redirect"
        swagger_ui_init_oauth: dict = {
            "usePkceWithAuthorizationCodeGrant": True,
            "clientId": auth_settings.OPENAPI_CLIENT_ID,
        }
    
        class Config:
            env_file = "settings.env"
    
    
    azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
        app_client_id=auth_settings.APP_CLIENT_ID,
        tenant_id=auth_settings.TENANT_ID,
        scopes={
            f"api://{auth_settings.APP_CLIENT_ID}/user_impersonation": "user_impersonation",
        },
    )
    
    
    question 
    opened by martin-greentrax 7
  • Working Solution for B2C MultiTenant

    Working Solution for B2C MultiTenant

    This is a working copy for B2C Multitenant. It's still not perfect but this is not a bad start to push this project forward! Love it! Thanks @JonasKs !

    Have to add a couple of more tests in future PRs.

    Hope you like it.

    opened by kristiqntashev 6
  • Auth with React

    Auth with React

    Describe the bug

    Hey all, Thanks for a great library! I have with success implemented the auth workflow with FastAPI and OpenAPI following your documentation I have a React frontend that needs to talk to the FastAPI backend and I have trouble getting it to work. I use the "@azure/msal-browser" package to get the access token in React, but when I send it in the header to FastAPI I get the following error:

    Traceback (most recent call last):
       File "/app/.local/lib/python3.9/site-packages/fastapi_azure_auth/auth.py", line 183, in __call__
         token = jwt.decode(
       File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 144, in decode
         raise JWTError(e)
     jose.exceptions.JWTError: Signature verification failed.
     INFO:     127.0.0.1:36092 - "GET /api/project/a833b3ff30 HTTP/1.1" 401 Unauthorized
    

    I have followed the same steps as for OpenAPI to setup my React SPA in Azure.

    To Reproduce

    1. Go to React UI
    2. Login to Azure
    3. Catch the token from the callback
    4. Set the token in the header
    5. Call FastAPI

    Stack trace

    [backend] INFO:     127.0.0.1:36088 - "OPTIONS /api/project/a833b3ff30 HTTP/1.1" 200 OK
    [backend] 2022-04-21 12:40:18,023 WARNING fastapi_azure_auth __call__() Malformed token received. null. Error: Error decoding token headers.
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 176, in _load
    [backend]     signing_input, crypto_segment = jwt.rsplit(b".", 1)
    [backend] ValueError: not enough values to unpack (expected 2, got 1)
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 183, in get_unverified_header
    [backend]     headers = jws.get_unverified_headers(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 109, in get_unverified_headers
    [backend]     return get_unverified_header(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 90, in get_unverified_header
    [backend]     header, claims, signing_input, signature = _load(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 180, in _load
    [backend]     raise JWSError("Not enough segments")
    [backend] jose.exceptions.JWSError: Not enough segments
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/fastapi_azure_auth/auth.py", line 136, in __call__
    [backend]     header: dict[str, str] = jwt.get_unverified_header(token=access_token) or {}
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 185, in get_unverified_header
    [backend]     raise JWTError("Error decoding token headers.")
    [backend] jose.exceptions.JWTError: Error decoding token headers.
    [backend] INFO:     127.0.0.1:36088 - "GET /api/project/a833b3ff30 HTTP/1.1" 401 Unauthorized
    [backend] 2022-04-21 12:40:18,150 WARNING fastapi_azure_auth __call__() Invalid token. Error: Signature verification failed.
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 262, in _verify_signature
    [backend]     raise JWSSignatureError()
    [backend] jose.exceptions.JWSSignatureError
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 142, in decode
    [backend]     payload = jws.verify(token, key, algorithms, verify=verify_signature)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 73, in verify
    [backend]     _verify_signature(signing_input, header, signature, key, algorithms)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 264, in _verify_signature
    [backend]     raise JWSError("Signature verification failed.")
    [backend] jose.exceptions.JWSError: Signature verification failed.
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/fastapi_azure_auth/auth.py", line 183, in __call__
    [backend]     token = jwt.decode(
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 144, in decode
    [backend]     raise JWTError(e)
    [backend] jose.exceptions.JWTError: Signature verification failed.
    [backend] INFO:     127.0.0.1:36088 - "GET /api/project/a833b3ff30 HTTP/1.1" 401 Unauthorized
    [backend] 2022-04-21 12:43:06,084 WARNING fastapi_azure_auth __call__() Malformed token received. null. Error: Error decoding token headers.
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 176, in _load
    [backend]     signing_input, crypto_segment = jwt.rsplit(b".", 1)
    [backend] ValueError: not enough values to unpack (expected 2, got 1)
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 183, in get_unverified_header
    [backend]     headers = jws.get_unverified_headers(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 109, in get_unverified_headers
    [backend]     return get_unverified_header(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 90, in get_unverified_header
    [backend]     header, claims, signing_input, signature = _load(token)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 180, in _load
    [backend]     raise JWSError("Not enough segments")
    [backend] jose.exceptions.JWSError: Not enough segments
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/fastapi_azure_auth/auth.py", line 136, in __call__
    [backend]     header: dict[str, str] = jwt.get_unverified_header(token=access_token) or {}
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 185, in get_unverified_header
    [backend]     raise JWTError("Error decoding token headers.")
    [backend] jose.exceptions.JWTError: Error decoding token headers.
    [backend] INFO:     127.0.0.1:36092 - "GET /api/project/a833b3ff30 HTTP/1.1" 401 Unauthorized
    [backend] 2022-04-21 12:43:06,334 WARNING fastapi_azure_auth __call__() Invalid token. Error: Signature verification failed.
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 262, in _verify_signature
    [backend]     raise JWSSignatureError()
    [backend] jose.exceptions.JWSSignatureError
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 142, in decode
    [backend]     payload = jws.verify(token, key, algorithms, verify=verify_signature)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 73, in verify
    [backend]     _verify_signature(signing_input, header, signature, key, algorithms)
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jws.py", line 264, in _verify_signature
    [backend]     raise JWSError("Signature verification failed.")
    [backend] jose.exceptions.JWSError: Signature verification failed.
    [backend] 
    [backend] During handling of the above exception, another exception occurred:
    [backend] 
    [backend] Traceback (most recent call last):
    [backend]   File "/app/.local/lib/python3.9/site-packages/fastapi_azure_auth/auth.py", line 183, in __call__
    [backend]     token = jwt.decode(
    [backend]   File "/app/.local/lib/python3.9/site-packages/jose/jwt.py", line 144, in decode
    [backend]     raise JWTError(e)
    [backend] jose.exceptions.JWTError: Signature verification failed.
    [backend] INFO:     127.0.0.1:36092 - "GET /api/project/a833b3ff30 HTTP/1.1" 401 Unauthorized
    
    
    question 
    opened by ocni-dtu 6
  • Scopes missing in /docs at each endpoint padlock

    Scopes missing in /docs at each endpoint padlock

    Describe the bug Scopes are missing if we select padlock symbol next to each api endpoint while trying to authorize. they do appear when we select authorize button at the top.

    To Reproduce use below azure scheme

    azure_scheme = SingleTenantAzureAuthorizationCodeBearer(
        app_client_id=settings.APP_CLIENT_ID,
        tenant_id=settings.TENANT_ID,
        scopes={
            f'api://{settings.APP_CLIENT_ID}/user_impersonation': 'user_impersonation',
        }
    )
    

    use this scheme as a dependency to your endpoint

    app.include_router(api_router, prefix=settings.API_V1_STR, dependencies=[Security(azure_scheme, scopes=['user_impersonation'])])
    

    go to /docs image

    you can see scopes when you click authorize button

    image

    if you click on padlock below scopes are missing

    image image

    and unable to authorize as I am getting below error..

    image

    waiting 
    opened by Pkumar-1988 5
  • Add OBO middleware [Feature request]

    Add OBO middleware [Feature request]

    Describe the feature you'd like It would be nice to have the framework allow more than one API scope. The case where this is needed, is if you have multiple business applications that need access to the same API. (For example like how both Teams and Outlook has access to your calendar). Features like this is supported in .NET frameworks by making a list of valid isuers and audiences, instead of enforcing just one (see this StackOverflow example for how the .NET AddJwtBearer middleware works)

    The "correct way" of dealing with these cases is to add middleware implementing the OBO ("On-Behalf-Of") flow. Usually this is handled by the client, but for third party applications and/or plugins, we cannot initiate OBO client-side.

    Additional context If I supply an access token with an audience that I've added to the my application's knownClientApplications list, the token validation should pass.

    enhancement 
    opened by lassebass 7
  • Improve tests

    Improve tests

    This PR is regarding only improving the UnitTests(adding some parametrized tests). Keep in mind behavior and the number of the tests are the same. Just removing not needed noise in the code.

    Directly reject it if doesn't look reasonable to you.

    opened by kristiqntashev 10
  • [Docs] Recipe to mock a logged-in user/Active Directory roles for testing

    [Docs] Recipe to mock a logged-in user/Active Directory roles for testing

    We are using this to keep the unit tests small and simple and I thought it may be helpful for others. Based on the username and the assigned ActiveDirectory role users have different permissions in the system. For a convenient way of testing this we can now mock any logged in user per-request:

    Example test:

    def test_readonly_user_may_not_write(client):
        with mockuser(user_name="[email protected]", roles=["foo.read"]):
            response = client.post("/somemodel", json={})
        assert response.status_code == 403
    
    

    The setup:

    in the routes:

    def prepare_user(request: Request, db: Session = Depends(get_db)):
        """
        On each request the azure authenticated user is used for local user lookup.
        When not found it will be created. The db user object is then stored in `request.state` for easy access.
        """
        if not hasattr(request.state, "user"):
            raise HTTPException(403, detail="No user information found in request object.")
        email: EmailStr = request.state.user.claims["preferred_username"]  # logged in azure user
        request.state.user_obj = UserRepo.get_or_create(db, email)
    
    router = APIRouter(dependencies=[Security(azure_scheme), Depends(prepare_user)])
    

    conftest.py

    class mockuser(ContextDecorator):
        """
        Mock any username and role(s) for api requests as if they would come directly from the real
        Azure Auth integration.
        """
    
        def __init__(self, user_name="[email protected]", roles=["Bar.readonly"]):
            self.user_name = user_name
            self.roles = roles
    
        def __enter__(self):
            def mock_prepare_user(request: fastapi.Request, db: Session = fastapi.Depends(get_db)):
                request.state.user = User(
                    claims={"preferred_username": self.user_name}, roles=self.roles,
                    aud="aud", tid="tid", access_token="123"
                )
                return prepare_user(request, db)
    
            fastapi_app.dependency_overrides[prepare_user] = mock_prepare_user
            fastapi_app_internal.dependency_overrides[prepare_user] = mock_prepare_user
    
        def __exit__(self, type, value, traceback):
            del fastapi_app.dependency_overrides[prepare_user]
            del fastapi_app_internal.dependency_overrides[prepare_user]
    
    
    class staffuser(mockuser):
        """Mock a staff user sending an api request."""
        def __init__(self):
            super().__init__(user_name="[email protected] roles=["yourcompany.superuser"])
    
    
    documentation enhancement 
    opened by martin-greentrax 2
  • [Feature request] oauth2-redirect outside local development

    [Feature request] oauth2-redirect outside local development

    The documentation is sufficient for building an API that runs in a local development environment - but I suppose that setting oauth2-redirect to a real domain instead of localhost is a common use case outside local development. Suppose the API is hosted somewhere (kubernetes, Azure Container Instances, Virtual Machine) - how should the oauth2-redirect URI now be changed - and what are some options on how to treat TLS in such a setting (as Azure App Registration will only allow https links as redirects). Maybe a section on production maturing would be helpful or maybe simply just clarifying that oauth2-redirect should be changed in a realistic production/TLS setting.

    documentation enhancement 
    opened by Christian-Schultz 9
Releases(3.5.1)
  • 3.5.1(Nov 10, 2022)

    Changes

    • Fixes README example scopes usage by @infomiho in https://github.com/Intility/fastapi-azure-auth/pull/102
    • chore: support python3.11 by @JonasKs in https://github.com/Intility/fastapi-azure-auth/pull/107

    New Contributors

    • @infomiho made their first contribution in https://github.com/Intility/fastapi-azure-auth/pull/102

    Full Changelog: https://github.com/Intility/fastapi-azure-auth/compare/3.5.0...3.5.1

    Source code(tar.gz)
    Source code(zip)
  • 3.5.0(Jul 8, 2022)

    Features:

    • B2CMultiTenantAuthorizationCodeBearer class which simplifies a multi-tenant B2C setup. ( #93 @kristiqntashev )

    Fix:

    • Make tid optional in the User model to support tokens sent from a B2C single-tenant ( #96 @marcinplatek )

    Github actions:

    • Fix caching ( @jonasks )
    Source code(tar.gz)
    Source code(zip)
  • 3.4.0(Apr 28, 2022)

  • 3.3.0(Mar 12, 2022)

    Features

    • Add setting to specify openid_config_url in AzureAuthorizationCodeBearerBase to add support for B2C tenants. ( #48 @robteeuwen and @JonasKs )
    • Use python-jose to load JWK, instead of x5c-keys loaded with cryptography. This add support for B2C tenants ( #48 @robteeuwen and @JonasKs )

    Other

    • Rewrite tests and clean up code ( #48 @JonasKs )
    Source code(tar.gz)
    Source code(zip)
  • 3.2.2(Mar 7, 2022)

    Fixes

    • Loosen cryptography version requirement, adding support for cryptography version 36, and future major bumps. ( @JonasKs, 63868904e9c7c2f3da31cdeefebec4884da402b4)
    Source code(tar.gz)
    Source code(zip)
  • 3.2.1(Mar 7, 2022)

    Bugfix

    • Python3.9.0 and Python3.9.1 has a bug with collections.abc.Callable, so this typing import has been changed to typing.Callable ( @ravaszf and @JonasKs #50 )
    Source code(tar.gz)
    Source code(zip)
  • 3.2.0(Jan 15, 2022)

    Features

    • auto_error flag/setting. When set to False an invalid token will return None instead of raising exception. (Β @bkmetzler and @JonasKs #44 )
      • This flag allows end users to configure multiple authentication paths, and reflects authentication classes shipped by FastAPI.
    Source code(tar.gz)
    Source code(zip)
  • 3.1.0(Oct 24, 2021)

  • 3.0.3(Oct 13, 2021)

  • 3.0.2(Oct 10, 2021)

    Fixes

    • Loosen FastAPI version requirement, allowing installation of this module on FastAPI 0.70.0+ ( @bmoore #28 )
      • Add Python3.10 and FastAPI==0.70.0 to pipeline matrix ( @jonasks #28 )
    Source code(tar.gz)
    Source code(zip)
  • 3.0.1(Oct 1, 2021)

    Fixes

    • Multi-tenant schema documentation ( #20 @sondrelg )
    • Multiple errors in the documentation ( #22 @daniwk )
    • Update cryptography requirement to ^35.0.0 and fix imports ( #26 @bmoore )
    Source code(tar.gz)
    Source code(zip)
  • 3.0.0(Sep 10, 2021)

    This release contains breaking changes for how to setup your application, but also a bunch of new features.

    The new documentation contains a full tutorial on how to configure Azure AD and FastAPI for both single- and multi-tenant applications.

    Features

    • Add v2 token support (and default) for single-tenant applications.
    • Full multi-tenant support
      • Option to provide a callable which returns valid iss(issuers), for those who has multi-tenant applications, but only for specific tenants

    Other

    • User object is reworked, now also contain access_token for easier Azure Graph implementation
    • Add support for denying requests with wrong scopes, when Securiy() is used (an alternativ to Depends())
    • Moved InvalidAuth to exceptions.py
    • Documentation for everything from role checks, guest users, locking down tenants etc.
    • No longer inheriting OAuth2AuthorizationCodeBearer, solving mypy errors.
    • Rename provider_config.py to openid_config.py and ProviderConfig() to OpenIdConfig()
    • Removal of pre-instance of provider_config due to OpenAPI authorization URL issues. This is now instanced on SingleTenantAzureAuthorizationCodeBearer or MultiTenantAzureAuthorizationCodeBearer.

    Features implemented in #16 ( @JonasKs )

    Source code(tar.gz)
    Source code(zip)
  • 3.0.0-rc1(Sep 1, 2021)

  • 2.0.1(Aug 18, 2021)

  • 2.0.0(Aug 18, 2021)

    Breaking changes

    • Removal of app parameter from AzureAuthorizationCodeBearer
    • AzureAuthorizationCodeBearer now returns a User object instead of a dictionary with claims

    Other

    • Documentation on how to create your own dependencies for checking roles, scp or similar
    • Add docs on how to load provider config on startup, it is no longer auto-loaded by AzureAuthorizationCodeBearer

    Related MR: #11 by @JonasKs


    Upgrade guide from v1 to v2 I strongly suggest reading the entire README.md again, as it's a bit more verbose compared to before.
    With that said, these are the steps you have to do in order to bump from v1 to v2:

    1. Remove app=app from your AzureAuthorizationCodeBearer() in main.py.
    2. If you have a dependencies.py file or similar, move the azure_scheme = AzureAuthorizationCodeBearer( ... ) to that file. 2.1. In your main.py, import azure_scheme from dependencies.py
    3. In your main.py file, load the provider config on startup:
    @app.on_event('startup')
    async def load_config() -> None:
        """
        Load config on startup.
        """
        await provider_config.load_config()
    
    1. If you've overwritten the default tenant_id, you can also add that to load_config()
    @app.on_event('startup')
    async def load_config() -> None:
        """
        Load config on startup.
        """
    +   provider_config.tenant_id = 'my-tenant-id' 
        await provider_config.load_config()
    
    Source code(tar.gz)
    Source code(zip)
  • 1.1.1(Aug 17, 2021)

    Features

    • Improve how key to validate token is chosen. It's now based on kid, instead of trying all keys ( #10 @JonasKs )

    Other/Improvements

    • More test cases for validating tokens ( #10 @JonasKs )
    • Test case for refreshing of provider config ( #10 @JonasKs )
    • More comments describing patterns and choices in the code ( #10 @JonasKs )
    Source code(tar.gz)
    Source code(zip)
  • 1.1.0(Aug 15, 2021)

    Features

    • User attached to the request state ( #6 @JonasKs )

    Improvements

    • py.typed added ( #6 @JonasKs )
    • codecov.yaml added for codecov updated in PRs ( #6 @JonasKs )
    • Better exception handling ( 49f0adbd19e8901025eea9d7c786bb8a3fc64dcc & 48bc86c0a51a61a4222f7db581c7ed037ee843d7 @JonasKs )
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0(Aug 13, 2021)

Owner
Intility
Intility is a fully managed platform service that covers all areas of your business. From your managed computer to the server hosting your applications.
Intility
Quick and simple security for Flask applications

Note This project is non maintained anymore. Consider the Flask-Security-Too project as an alternative. Flask-Security It quickly adds security featur

Matt Wright 1.6k Dec 19, 2022
examify-io is an online examination system that offers automatic grading , exam statistics , proctoring and programming tests , multiple user roles

examify-io is an online examination system that offers automatic grading , exam statistics , proctoring and programming tests , multiple user roles ( Examiner , Supervisor , Student )

Ameer Nasser 4 Oct 28, 2021
A JOSE implementation in Python

python-jose A JOSE implementation in Python Docs are available on ReadTheDocs. The JavaScript Object Signing and Encryption (JOSE) technologies - JSON

Michael Davis 1.2k Dec 28, 2022
Pingo provides a uniform API to program devices like the Raspberry Pi, BeagleBone Black, pcDuino etc.

Pingo provides a uniform API to program devices like the Raspberry Pi, BeagleBone Black, pcDuino etc. just like the Python DBAPI provides an uniform API for database programming in Python.

Garoa Hacker Clube 12 May 22, 2022
Out-of-the-box support register, sign in, email verification and password recovery workflows for websites based on Django and MongoDB

Using djmongoauth What is it? djmongoauth provides out-of-the-box support for basic user management and additional operations including user registrat

hao 3 Oct 21, 2021
Brute force a JWT token. Script uses multithreading.

JWT BF Brute force a JWT token. Script uses multithreading. Tested on Kali Linux v2021.4 (64-bit). Made for educational purposes. I hope it will help!

Ivan Ε incek 5 Dec 02, 2022
Web authentication testing framework

What is this This is a framework designed to test authentication for web applications. While web proxies like ZAProxy and Burpsuite allow authenticate

OWASP 88 Jan 01, 2023
Todo app with authentication system.

todo list web app with authentication system. User can register, login, logout. User can login and create, delete, update task Home Page here you will

Anurag verma 3 Aug 18, 2022
A recipe sharing API built using Django rest framework.

Recipe Sharing API This is the backend API for the recipe sharing platform at https://mesob-recipe.netlify.app/ This API allows users to share recipes

Hannah 21 Dec 30, 2022
Luca Security Concept

Luca Security Concept This is the document source of luca's security concept. Please go here for the HTML version: https://luca-app.de/securityconcept

luca 43 Oct 22, 2022
This script helps you log in to your LMS account and enter the currently running session

This script helps you log in to your LMS account and enter the currently running session, all in a second

Ali Ebrahimi 5 Sep 01, 2022
Minimal authorization through OO design and pure Ruby classes

Pundit Pundit provides a set of helpers which guide you in leveraging regular Ruby classes and object oriented design patterns to build a simple, robu

Varvet 7.8k Jan 02, 2023
Djagno grpc authentication service with jwt auth

Django gRPC authentication service STEP 1: Install packages pip install -r requirements.txt STEP 2: Make migrations and migrate python manage.py makem

Saeed Hassani Borzadaran 3 May 16, 2022
row level security for FastAPI framework

Row Level Permissions for FastAPI While trying out the excellent FastApi framework there was one peace missing for me: an easy, declarative way to def

Holger Frey 315 Dec 25, 2022
Login System Using Django

Login System Django

Nandini Chhajed 6 Dec 12, 2021
python-social-auth and oauth2 support for django-rest-framework

Django REST Framework Social OAuth2 This module provides OAuth2 social authentication support for applications in Django REST Framework. The aim of th

1k Dec 22, 2022
A simple model based API maker written in Python and based on Django and Django REST Framework

Fast DRF Fast DRF is a small library for making API faster with Django and Django REST Framework. It's easy and configurable. Full Documentation here

Mohammad Ashraful Islam 18 Oct 05, 2022
Accounts for Django made beautifully simple

Django Userena Userena is a Django application that supplies your Django project with full account management. It's a fully customizable application t

Bread & Pepper 1.3k Sep 18, 2022
REST implementation of Django authentication system.

djoser REST implementation of Django authentication system. djoser library provides a set of Django Rest Framework views to handle basic actions such

Sunscrapers 2.2k Jan 01, 2023
Library - Recent and favorite documents

Thingy Thingy is used to quickly access recent and favorite documents. It's an XApp so it can work in any distribution and many desktop environments (

Linux Mint 23 Sep 11, 2022