Open-source project
vibber-ai/fastapi-azure-auth avatar
vibber-ai/fastapi-azure-auth

fastapi-azure-auth: Entra ID bearer schemes inside a FastAPI app

Easy and secure implementation of Azure Entra ID (previously AD) for your FastAPI APIs 🔒 B2C, single- and multi-tenant support.

681 stars79 forksPythonMIT

At a glance

What is it?
fastapi-azure-auth is a Python package that wires Azure Entra ID authentication into FastAPI through three bearer scheme classes, one each for single-tenant, multi-tenant and B2C applications. It handles OpenID configuration, scope checks and the Swagger UI login flow, but the issuer check in multi-tenant mode is off unless you turn it on.
Who is it for?
Use fastapi-azure-auth if your FastAPI service sits behind Entra ID and you want a scoped security dependency plus a working Authorize button in the OpenAPI docs, rather than writing OpenID discovery and token validation yourself. Do not adopt it for non-Entra identity providers, and treat the multi-tenant example in the setup guide as a warning rather than a template, since validate_iss=False skips issuer validation.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 113 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three scheme classes decide how much of Entra ID you trust

The package exposes three authentication classes from fastapi_azure_auth.auth, and the choice between them is the first real decision you make. SingleTenantAzureAuthorizationCodeBearer takes a tenant_id, so tokens are only accepted from one directory tenant. MultiTenantAzureAuthorizationCodeBearer omits the tenant and instead works across tenants, which is what you need for a product sold to many organisations. B2CMultiTenantAuthorizationCodeBearer covers the separate Azure AD B2C tenant model. The multi-tenant class takes a validate_iss argument, and the setup guide shows it set to False, with a pointer to an iss_callable setting for the opposite behaviour. That default matters more than it looks. Turning issuer validation off means a token minted by a different issuer can pass, unless you supply your own iss_callable. The documentation also describes examples that lock APIs down to specific scopes, tenants and roles, so the granularity is there if you configure it.

The scheme plugs in as an ordinary FastAPI security dependency

Once constructed, the scheme object is used the way any FastAPI dependency is used, with Security() or Depends(). The setup guide attaches it to a router with an explicit scope list:

python
# 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'])])

The scope name in that list has to match the key of the scopes mapping you passed to the scheme, which in the example is an API permission identifier of the form api://<client id>/user_impersonation mapped to the short name user_impersonation. Because the dependency is attached at the router level, every route under that prefix inherits the check, and each route also receives the decoded token payload as a dependency value. The mapping between application scope names and Azure scope identifiers is done once, in the scheme constructor, so the rest of your code only deals with the short names. That is the main ergonomic argument for the package: the Azure-specific string handling stays in one file.

OpenID configuration is fetched once at startup

The scheme holds an openid_config object, and the documented startup step loads it before the first request arrives:

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

The guide marks this step optional but recommended, which tells you the server can still start without it. What it does tell you is that the package fetches and caches the OpenID provider configuration rather than assuming a fixed set of endpoints and keys, which is the part you would otherwise have to write. The most recent release, 5.3.0 from 2026-07-02, is described as adding httpx client configuration and preventing concurrent refreshes, and 5.2.1 the same day brought a new organisation, ruff, uv and dependency updates. Both of those point at the same area of the code: the HTTP client used to talk to the identity provider and the refresh of that cached configuration. The last push to the repository was on 2026-07-02, and the release notes in the repository show 5.2.0 from 2025-07-25 adding customizable scheme names for the OpenAPI documentation.

Swagger UI can run the authorization code flow on its own

A detail that separates this from a hand-rolled dependency is that the OpenAPI page gets a working login. To make that happen, your FastAPI app has to point Swagger UI at an OAuth2 redirect endpoint and pass the client id:

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

With that in place the OpenAPI documentation shows an Authorize button, and the person using the docs can pick which scopes to authenticate with based on your configuration. The client id used here is separate from the one the scheme validates, which the setup guide names settings.OPENAPI_CLIENT_ID against settings.APP_CLIENT_ID, so you may need two registrations or two values depending on your setup. The usePkceWithAuthorizationCodeGrant flag means the browser flow uses PKCE. One practical detail from the setup guide is easy to miss: CORS has to be enabled for your local environment, such as http://localhost:8000, or the browser side of the flow fails.

Installing the package and wiring the first protected route

Installation is a single command, and the package is on PyPI as fastapi-azure-auth:

bash
uv add fastapi-azure-auth

Then construct the scheme. For a single-tenant application you pass the client id, the tenant id and the scope mapping:

python
# 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',
    }
)

For a multi-tenant application the tenant id is dropped and issuer validation appears:

python
# 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
)

The multi-tenant variant is the one to think about twice, because as written it accepts tokens without checking the issuer. The documentation links an iss_callable setting for validating the iss against a specific set of tenants. If your application already uses Azure AD B2C, the third class, B2CMultiTenantAuthorizationCodeBearer, follows the same pattern with the B2C tenant configured instead of tenant_id.

The dependency floor and what the package brings with it

The packaging metadata tells you what the library expects from your environment. The project requires Python 3.11 or newer, and the classifiers list 3.11 through 3.14, so older interpreters are out. It depends on fastapi 0.111.1 or later, along with anyio 4.4.0 or later, cryptography 48.0.1 or later, httpx2 2.0.0 or later, pydantic 2.6.2 or later and pyjwt with a range that stops before 3.0. The presence of pyjwt and cryptography is what a JWT validation path needs, and anyio plus httpx2 cover the async HTTP side of talking to the identity provider. The keyword list mentions starlette, asyncio and trio, which reflects that the package is designed for async ASGI applications. The code is MIT licensed, so there is no commercial restriction on using it in a product, and the classifiers mark the project as Production/Stable. In the repository, the package source sits in fastapi_azure_auth/, next to a demo_project/ directory, a docs/ directory and a tests/ suite, with pytest.ini, mypy.ini and .flake8 for the checks and uv.lock for reproducible installs.

Where the documentation stops and you have to read further

The setup guide in the repository is explicitly a short summary, and it says so, pointing first time users at the full documentation site for a tutorial and a settings reference. Several things are therefore out of reach from the summary alone. The complete settings reference is where the token lifetime handling, the refresh logic and the role and tenant checks are documented, and the guide itself only shows the shape of the arguments. The summary also does not say how the library distinguishes a B2C token from an Entra ID token beyond the separate class, nor does it document what happens when the OpenID configuration endpoint is unreachable at startup. Another honest limit is that the multi-tenant sample is presented as a starting point with validate_iss=False, which is exactly the setting you should change before putting it in front of real users. For the repository itself, the SECURITY.md file gives you the place to report problems, and CONTRIBUTING.md covers how to send changes.

Editorial conclusion

Use fastapi-azure-auth if your FastAPI service sits behind Entra ID and you want a scoped security dependency plus a working Authorize button in the OpenAPI docs, rather than writing OpenID discovery and token validation yourself. Do not adopt it for non-Entra identity providers, and treat the multi-tenant example in the setup guide as a warning rather than a template, since validate_iss=False skips issuer validation. Verify first that your app registration matches the scheme class you choose, and that you load the OpenID configuration at startup through azure_scheme.openid_config.load_config().

Frequently asked questions

How do I install fastapi-azure-auth?

The setup guide gives one command, uv add fastapi-azure-auth, and the package is published on PyPI as fastapi-azure-auth. It requires Python 3.11 or newer and FastAPI 0.111.1 or later.

What is the difference between the single-tenant and multi-tenant bearer schemes?

SingleTenantAzureAuthorizationCodeBearer takes a tenant_id and only accepts tokens from that tenant, while MultiTenantAzureAuthorizationCodeBearer works across tenants and adds a validate_iss argument. The setup guide sets validate_iss=False and points to an iss_callable setting to validate the issuer yourself.

Does fastapi-azure-auth add a login button to the OpenAPI docs?

Yes. Your FastAPI app needs swagger_ui_oauth2_redirect_url and swagger_ui_init_oauth with a clientId, and the OpenAPI page then shows an Authorize button where the user can pick which scopes to authenticate with.

Do I have to load the OpenID configuration on startup?

The setup guide marks it optional but recommended, and shows a startup handler that awaits azure_scheme.openid_config.load_config(). The release notes for 5.3.0 mention httpx client configuration and preventing concurrent refreshes, which is the area this step belongs to.

What licence is fastapi-azure-auth released under?

The package is MIT licensed, and the classifiers in the packaging metadata list it as License :: OSI Approved :: MIT License. The repository also carries a SECURITY.md file for reporting security issues.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. vibber-ai/fastapi-azure-auth on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/vibber-ai-fastapi-azure-auth.svg)](https://hysenlabs.com/projects/vibber-ai-fastapi-azure-auth)

Was this useful?