Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 7 additions & 17 deletions .github/workflows/codeql-analysis.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,16 @@ name: "CodeQL"

on:
push:
branches: [ main ]
branches: [main]
schedule:
- cron: '0 07 * * 1'
- cron: "0 07 * * 1"

jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
security-events: write
strategy:
fail-fast: false
steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand All @@ -24,22 +22,14 @@ jobs:
with:
python-version: "3.11"

- name: Install poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
virtualenvs-create: true
virtualenvs-in-project: true

- name: Load cached venv
uses: actions/cache@v4.2.3
id: cache-venv
with:
path: .venv
key: ${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}-0
activate-environment: true
enable-cache: true

- name: Install dependencies
run: poetry install --no-interaction --no-root
if: steps.cache-venv.outputs.cache-hit != 'true'
run: uv sync --locked --all-extras --dev

- name: Initialize CodeQL
uses: github/codeql-action/init@v2
Expand Down
44 changes: 18 additions & 26 deletions .github/workflows/coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,21 @@ jobs:
codecov:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11.0"
- name: Install poetry
uses: snok/install-poetry@v1
with:
virtualenvs-create: true
virtualenvs-in-project: true
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v4.2.3
with:
path: .venv
key: venv-${{ runner.os }}-3.11.0-${{ hashFiles('**/poetry.lock') }}-0
- name: Install dependencies
run: poetry install
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
- name: Test with pytest
run: poetry run pytest --cov=fastapi_azure_auth tests/ --verbose --assert=plain --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v5
with:
file: ./coverage.xml
fail_ci_if_error: true
token: ${{ secrets.CODECOV_TOKEN }}
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
activate-environment: true
enable-cache: true
- name: Install dependencies
run: uv sync --locked --all-extras --dev
- name: Test with pytest
run: uv run pytest --cov=fastapi_azure_auth tests/ --verbose --assert=plain --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v5
with:
files: ./coverage.xml
fail_ci_if_error: true
10 changes: 7 additions & 3 deletions .github/workflows/publish_to_pypi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: 3.11
- uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
activate-environment: true
enable-cache: true
- name: Publish to pypi
run: |
poetry config pypi-token.pypi ${{ secrets.PYPI_TOKEN }}
poetry publish --build --no-interaction
uv build
uv publish --token ${{ secrets.PYPI_TOKEN }}
32 changes: 18 additions & 14 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,41 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: 3.12
- run: python -m pip install pre-commit
- run: pre-commit run --all-files
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
activate-environment: true
enable-cache: true
- name: Install dependencies
run: uv sync --locked --all-extras --dev
- run: uv run pre-commit run --all-files
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: [ "3.9", "3.10", "3.11", "3.12" ]
fastapi-version: [ "0.103.2", "0.111.1"]
python-version: ["3.9", "3.10", "3.11", "3.12"]
fastapi-version: ["0.103.2", "0.111.1"]
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
uses: snok/install-poetry@v1
- name: Install uv
uses: astral-sh/setup-uv@v6
with:
virtualenvs-create: true
virtualenvs-in-project: true
activate-environment: true
enable-cache: true
- name: Install dependencies
run: poetry install --no-interaction --no-root
- name: Install package
run: poetry install --no-interaction
run: uv sync --locked --all-extras --dev
- name: Install FastAPI ${{ matrix.fastapi-version }}
run: |
source .venv/bin/activate
poetry add "fastapi==${{ matrix.fastapi-version }}"
uv add "fastapi==${{ matrix.fastapi-version }}"
- name: Run tests
run: |
source .venv/bin/activate
poetry run pytest --cov=fastapi_azure_auth --verbose --assert=plain
poetry run coverage report
uv run pytest --cov=fastapi_azure_auth --verbose --assert=plain
uv run coverage report
39 changes: 10 additions & 29 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
exclude: README.md
repos:
- repo: https://github.com/ambv/black
rev: '23.9.1'
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.12.5
hooks:
- id: black
args: ['--quiet']
- id: ruff-check
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
Expand All @@ -20,34 +21,14 @@ repos:
hooks:
- id: flake8
additional_dependencies: [
Comment on lines 22 to 23
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you keeping these flake8 hooks?

'flake8-bugbear==23.9.16', # Looks for likely bugs and design problems
'flake8-comprehensions==3.14.0', # Looks for unnecessary generator functions that can be converted to list comprehensions
'flake8-deprecated==2.1', # Looks for method deprecations
'flake8-use-fstring==1.4', # Enforces use of f-strings over .format and %s
'flake8-print==5.0.0', # Checks for print statements
'flake8-docstrings==1.7.0', # Verifies that all functions/methods have docstrings
'flake8-annotations==3.0.1', # Enforces type annotation
]
args: ['--enable-extensions=G']
- repo: https://github.com/asottile/pyupgrade
rev: v3.14.0
hooks:
- id: pyupgrade
args: ["--py36-plus"]
- repo: https://github.com/pycqa/isort
rev: 5.12.0
hooks:
- id: isort
"flake8-deprecated==2.1", # Looks for method deprecations
"flake8-use-fstring==1.4", # Enforces use of f-strings over .format and %s
Copy link

@Kilo59 Kilo59 Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

flake8-use-fstring can be handled by the UP032 rule.
You don't need to keep flake8 around for this.
https://docs.astral.sh/ruff/rules/f-string/

]
args: ["--enable-extensions=G"]
- repo: https://github.com/pre-commit/mirrors-mypy
rev: "v1.15.0"
hooks:
- id: mypy
exclude: "test_*"
additional_dependencies:
[
fastapi,
pydantic,
pydantic-settings,
starlette,
httpx
]
[fastapi, pydantic, pydantic-settings, starlette, httpx]
12 changes: 6 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ This package is open to contributions 👏
To contribute, please follow these steps:

1. Create an issue explaining what you'd like to fix or add. This way, we can approve and discuss the
solution before any time is spent on developing it.
solution before any time is spent on developing it.
2. Fork the upstream repository into a personal account.
3. Install [poetry](https://python-poetry.org/), and install all dependencies using ``poetry install --with dev``
4. Activate the environment by running ``poetry shell``
5. Install [pre-commit](https://pre-commit.com/) (for project linting) by running ``pre-commit install``
3. Install [uv](https://docs.astral.sh/uv/), and install all dependencies using `uv sync`
4. [Optional] Activate the environment by running `source .venv/bin/activate`
5. Install [pre-commit](https://pre-commit.com/) (for project linting) by running `uv run pre-commit install`
6. Create a new branch for your changes.
7. Create and run tests with full coverage by running `poetry run pytest --cov fastapi_azure_auth --cov-report=term-missing`
7. Create and run tests with full coverage by running `uv run pytest --cov fastapi_azure_auth --cov-report=term-missing`
8. Push the topic branch to your personal fork.
9. Run `pre-commit run --all-files` locally to ensure proper linting.
9. Run `uv run pre-commit run --all-files` locally to ensure proper linting.
10. Create a pull request to the intility repository with a detailed summary of your changes and what motivated the change.

If you need a more detailed walk through, please see this
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ For a more in-depth tutorial and settings reference you should read the
pip install fastapi-azure-auth
# or
poetry add fastapi-azure-auth
# or
uv add fastapi-azure-auth
```

#### 2. Configure your FastAPI app
Expand Down
2 changes: 1 addition & 1 deletion demo_project/schemas/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@


class HelloWorldResponse(BaseModel):
hello: str = Field(..., description='What we\'re saying hello to')
hello: str = Field(..., description="What we're saying hello to")
user: User = Field(..., description='The user object')


Expand Down
2 changes: 2 additions & 0 deletions docs/docs/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ You can install FastAPI-Azure-Auth like any other package on PyPI:
pip install fastapi-azure-auth
# OR
poetry add fastapi-azure-auth
# OR
uv add fastapi-azure-auth
```

:::info
Expand Down
8 changes: 4 additions & 4 deletions fastapi_azure_auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ def __init__(
openapi_token_url: Optional[str] = None,
openid_config_url: Optional[str] = None,
openapi_description: Optional[str] = None,
scheme_name: str = "AzureAuthorizationCodeBearerBase",
scheme_name: str = 'AzureAuthorizationCodeBearerBase',
) -> None:
"""
Initialize settings.
Expand Down Expand Up @@ -301,7 +301,7 @@ def __init__(
openapi_authorization_url: Optional[str] = None,
openapi_token_url: Optional[str] = None,
openapi_description: Optional[str] = None,
scheme_name: str = "AzureAD_PKCE_single_tenant",
scheme_name: str = 'AzureAD_PKCE_single_tenant',
) -> None:
"""
Initialize settings for a single tenant application.
Expand Down Expand Up @@ -370,7 +370,7 @@ def __init__(
openapi_authorization_url: Optional[str] = None,
openapi_token_url: Optional[str] = None,
openapi_description: Optional[str] = None,
scheme_name: str = "AzureAD_PKCE_multi_tenant",
scheme_name: str = 'AzureAD_PKCE_multi_tenant',
) -> None:
"""
Initialize settings for a multi-tenant application.
Expand Down Expand Up @@ -446,7 +446,7 @@ def __init__(
openapi_authorization_url: Optional[str] = None,
openapi_token_url: Optional[str] = None,
openapi_description: Optional[str] = None,
scheme_name: str = "AzureAD_PKCE_B2C_multi_tenant",
scheme_name: str = 'AzureAD_PKCE_B2C_multi_tenant',
) -> None:
"""
Initialize settings for a B2C multi-tenant application.
Expand Down
20 changes: 10 additions & 10 deletions fastapi_azure_auth/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class InvalidRequestHttp(HTTPException):

def __init__(self, detail: str) -> None:
super().__init__(
status_code=status.HTTP_400_BAD_REQUEST, detail={"error": "invalid_request", "message": detail}
status_code=status.HTTP_400_BAD_REQUEST, detail={'error': 'invalid_request', 'message': detail}
)


Expand All @@ -18,7 +18,7 @@ class InvalidRequestWebSocket(WebSocketException):

def __init__(self, detail: str) -> None:
super().__init__(
code=status.WS_1008_POLICY_VIOLATION, reason=str({"error": "invalid_request", "message": detail})
code=status.WS_1008_POLICY_VIOLATION, reason=str({'error': 'invalid_request', 'message': detail})
)


Expand All @@ -33,8 +33,8 @@ def __init__(self, detail: str, authorization_url: str | None = None, client_id:
header_value += f', client_id="{client_id}"'
super().__init__(
status_code=status.HTTP_401_UNAUTHORIZED,
detail={"error": "invalid_token", "message": detail},
headers={"WWW-Authenticate": header_value},
detail={'error': 'invalid_token', 'message': detail},
headers={'WWW-Authenticate': header_value},
)


Expand All @@ -43,7 +43,7 @@ class UnauthorizedWebSocket(WebSocketException):

def __init__(self, detail: str) -> None:
super().__init__(
code=status.WS_1008_POLICY_VIOLATION, reason=str({"error": "invalid_token", "message": detail})
code=status.WS_1008_POLICY_VIOLATION, reason=str({'error': 'invalid_token', 'message': detail})
)


Expand All @@ -53,8 +53,8 @@ class ForbiddenHttp(HTTPException):
def __init__(self, detail: str) -> None:
super().__init__(
status_code=status.HTTP_403_FORBIDDEN,
detail={"error": "insufficient_scope", "message": detail},
headers={"WWW-Authenticate": "Bearer"},
detail={'error': 'insufficient_scope', 'message': detail},
headers={'WWW-Authenticate': 'Bearer'},
)


Expand All @@ -63,7 +63,7 @@ class ForbiddenWebSocket(WebSocketException):

def __init__(self, detail: str) -> None:
super().__init__(
code=status.WS_1008_POLICY_VIOLATION, reason=str({"error": "insufficient_scope", "message": detail})
code=status.WS_1008_POLICY_VIOLATION, reason=str({'error': 'insufficient_scope', 'message': detail})
)


Expand Down Expand Up @@ -112,13 +112,13 @@ def Unauthorized(
detail: str, request: HTTPConnection, authorization_url: str | None = None, client_id: str | None = None
) -> UnauthorizedHttp | UnauthorizedWebSocket:
"""Factory function for unauthorized exceptions"""
if request.scope["type"] == "http":
if request.scope['type'] == 'http':
return UnauthorizedHttp(detail, authorization_url, client_id)
return UnauthorizedWebSocket(detail)


def Forbidden(detail: str, request: HTTPConnection) -> ForbiddenHttp | ForbiddenWebSocket:
"""Factory function for forbidden exceptions"""
if request.scope["type"] == "http":
if request.scope['type'] == 'http':
return ForbiddenHttp(detail)
return ForbiddenWebSocket(detail)
2 changes: 1 addition & 1 deletion fastapi_azure_auth/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class Claims(BaseModel):
description='The primary username that represents the user. Only available in V2.0 tokens',
)

@field_validator('scp', mode="before")
@field_validator('scp', mode='before')
def scopes_to_list(cls, v: object) -> object:
"""
Validator on the scope attribute that convert the space separated list
Expand Down
Loading
Loading