Skip to content

audit cleanup + mypy strict pass#20

Merged
mdfarhankc merged 2 commits into
mainfrom
fix/audit-cleanup-v0.9.2
May 14, 2026
Merged

audit cleanup + mypy strict pass#20
mdfarhankc merged 2 commits into
mainfrom
fix/audit-cleanup-v0.9.2

Conversation

@mdfarhankc
Copy link
Copy Markdown
Owner

@mdfarhankc mdfarhankc commented May 14, 2026

Summary

  • Audit cleanup: refresh-token endpoint rate limit, UUID consistency fix, real LoginResponse class, alpha→beta classifier, dynamic versioning via hatch, dep floors bumped, [tool.pytest] flat config, pytest-cov added.
  • mypy --strict pass across middleware, ratelimit, tokens, oauth, flows, dependencies, routers; per-file overrides on the two SQLAlchemy adapter files for known stub limitations.
  • Fixed a real semantic bug in router/oauth.py — unlink was passing user.id (UUID) where the provider's provider_user_id (string) is expected, so it would never match in practice.

Folds into the v0.10.0 release (no v0.9.2 release on PyPI).

Test plan

  • uv run ruff format --check . — clean
  • uv run ruff check . — clean
  • uv run pytest tests/ — 189 passed, 1 skipped (bcrypt)

Summary by CodeRabbit

Release Notes v0.9.2

  • New Features

    • Added rate-limit configuration for OAuth token refresh endpoints
  • Bug Fixes

    • Fixed OAuth account unlinking to use correct provider identifier
    • Corrected user ID handling in token persistence (UUID vs string)
  • Improvements

    • Enhanced type safety with stricter validation throughout the library
    • Improved error handling for token creation when secrets are unset
    • Strengthened LoginResponse model typing
  • Documentation

    • Updated API reference documentation with new rate-limit setting

Review Change Stack

Rate-limit /auth/refresh (default 30 req/min) so a stolen refresh
token can't be ground for fresh access tokens, and pass user.id
through to RefreshToken instead of stringifying it. Replace the
dynamic LoginResponse model with a real subclass of TokenPair so
static checkers see the user kwarg.

Switch to dynamic versioning via hatch, reorder pyproject sections,
move pytest config to [tool.pytest], bump all dependency floors to
current stable, and bump the Development Status classifier from
Alpha to Beta. Adds Operating System :: OS Independent and pulls in
pytest-cov for contributors.
Adds the missing parameter and return annotations, replaces bare `dict`
with `dict[str, Any]`, tightens `hash_password(algorithm=...)` at the
flow call sites to `Literal["argon2id", "bcrypt"]`, and types the ASGI
middleware constructors with `ASGIApp` / `RequestResponseEndpoint`.

Mixin methods accessed through `AbstractUserAdapter` (`assign_role`,
`get_oauth_account`, etc.) are now narrowed via `cast()` to the right
mixin at each call site in admin, oauth, and require_role. Passkey
routes drop the inline `assert`s in favour of `cast()` over the values
already enforced by FullAuthConfig at construction time, so `python -O`
doesn't quietly remove them.

Also fixes a real bug: `DELETE /oauth/accounts/{provider}` was calling
`delete_oauth_account(provider, user.id)`, passing the local user UUID
where the provider's `provider_user_id` was expected. The query never
matched, so unlinking silently no-op'd. The route now resolves the
right account first and returns 404 when the user doesn't have one.

SQLAlchemy 2.0 / SQLModel column-comparison stub limitations
(`where(self.user_model.id == user_id)` types as `bool`, not
`ColumnElement[bool]`) are scoped to the two adapter modules via a
focused `[[tool.mypy.overrides]]` block so the rest of the codebase
stays strict.

`uv run mypy --strict fastapi_fullauth` now passes 0/0 and CI runs it
on every push and PR. 189 tests still pass.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

📝 Walkthrough

Walkthrough

This PR implements version 0.9.2 with comprehensive type safety improvements across the codebase, adds a new refresh-token rate-limiting feature, refactors adapter patterns for type correctness, and introduces a LoginResponse model with optional user inclusion. The changes also standardize refresh-token user_id storage and enforce strict mypy type-checking in CI.

Changes

Type Safety and Refresh Rate Limiting (0.9.2)

Layer / File(s) Summary
Type infrastructure and core imports
fastapi_fullauth/adapters/*.py, fastapi_fullauth/backends/cookie.py, fastapi_fullauth/core/..., fastapi_fullauth/dependencies/..., fastapi_fullauth/flows/..., fastapi_fullauth/middleware/..., fastapi_fullauth/oauth/..., fastapi_fullauth/router/passkey.py
TYPE_CHECKING guards, forward references (FullAuthConfig, TokenEngine, httpx), and typed imports (Any, Literal, Callable, Coroutine) established across modules to enable strict type-checking without runtime overhead.
Core token and crypto type enforcement
fastapi_fullauth/core/tokens.py, fastapi_fullauth/core/crypto.py, fastapi_fullauth/core/redis_blacklist.py
Runtime SECRET_KEY validation guards added to create_access_token, create_refresh_token, and decode_token. Token payloads typed as dict[str, Any], bcrypt operations explicitly return bool, and Redis blacklist result wrapped in bool().
Configuration and refresh rate limiting
fastapi_fullauth/config.py, pyproject.toml, .github/workflows/ci.yml
AUTH_RATE_LIMIT_REFRESH setting added (default 30), dynamic versioning via fastapi_fullauth/init.py, dependencies bumped, pytest config migrated to [tool.pytest], and mypy --strict enforced in CI with adapter module overrides.
Rate limiter setup and refresh endpoint protection
fastapi_fullauth/protection/ratelimit.py, fastapi_fullauth/router/auth.py, tests/test_auth.py
New "refresh" rate limiter wired in AuthRateLimiter using AUTH_RATE_LIMIT_REFRESH. /refresh endpoint updated to accept Request, compute client_ip, and check_auth_rate_limit("refresh", client_ip) before token rotation. Test verifies 429 on second request.
Dependency factory and RBAC typing
fastapi_fullauth/dependencies/current_user.py, fastapi_fullauth/dependencies/require_role.py, fastapi_fullauth/router/admin.py
Dependency factories (get_current_user_dependency, etc.) now return Callable[..., Coroutine[Any, Any, UserSchemaType]]. require_permission casts fullauth.adapter to PermissionAdapterMixin before calling get_user_permissions. Admin routes cast adapter for role/permission operations.
OAuth flow typing and adapter casting
fastapi_fullauth/oauth/base.py, fastapi_fullauth/oauth/github.py, fastapi_fullauth/oauth/google.py, fastapi_fullauth/flows/oauth.py, fastapi_fullauth/router/oauth.py
Abstract methods and implementations typed with dict[str, Any] for tokens/exchange. verify_oauth_state extracts redirect_uri from extra. OAuth account operations cast adapter to OAuthAdapterMixin. GitHub provider adds error parsing; Google provider stores tokens in typed variable. OAuth unlink now uses provider_user_id correctly.
Auth router login and refresh flows
fastapi_fullauth/router/auth.py
/login handler extracts identifier and password via model_dump() cast to BaseModel. /refresh accepts Request, enforces rate limiting via client IP. Both flows store RefreshToken.user_id using user.id directly instead of string conversion variable.
Passkey and login response modeling
fastapi_fullauth/router/_models.py, fastapi_fullauth/flows/passkey.py, fastapi_fullauth/router/passkey.py
LoginResponse model introduced (TokenPair subclass with optional user field). Passkey registration/authentication flows use dict[str, Any] return/parameter types, explicit config casting, and AuthenticatorTransport conversion. Refresh token persistence in passkey completion uses user.id directly.
Hash algorithm typing constraints
fastapi_fullauth/flows/login.py, fastapi_fullauth/flows/register.py, fastapi_fullauth/flows/change_password.py, fastapi_fullauth/flows/password_reset.py, fastapi_fullauth/flows/set_password.py, fastapi_fullauth/utils.py
All hash_algorithm parameters constrained to Literal["argon2id", "bcrypt"] instead of generic str.
Middleware and response type annotations
fastapi_fullauth/middleware/csrf.py, fastapi_fullauth/middleware/security_headers.py, fastapi_fullauth/fullauth.py
CSRFMiddleware and SecurityHeadersMiddleware now typed with ASGIApp for app parameter and Literal for cookie_samesite. CSRF_SECRET requirement enforced via RuntimeError. get_custom_claims return type refined to dict[str, Any].
Challenge store and lockout protection typing
fastapi_fullauth/core/challenges.py, fastapi_fullauth/protection/lockout.py, fastapi_fullauth/protection/ratelimit.py
create_challenge_store and create_lockout updated with forward-referenced FullAuthConfig parameters. create_rate_limiter return type union (RateLimiter
Documentation and versioning
fastapi_fullauth/__init__.py, CHANGELOG.md, fastapi_fullauth/.agents/skills/fastapi-fullauth/references/api-reference.md
Version bumped 0.9.1 → 0.9.2. Changelog documents refresh rate limiting, UUID handling fixes, OAuth unlinking, strict mypy enforcement, and type improvements. API reference documents AUTH_RATE_LIMIT_REFRESH setting.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • mdfarhankc/fastapi-fullauth#8: Introduces LoginResponse model with optional user field used in this PR's auth/OAuth route wiring.
  • mdfarhankc/fastapi-fullauth#18: Adds py.typed marker and mypy dev dependency; this PR enforces strict mypy in CI and continues type-safety hardening.
  • mdfarhankc/fastapi-fullauth#9: Introduces AuthRateLimiter and create_rate_limiter factory; this PR extends it with dedicated "refresh" limiter driven by AUTH_RATE_LIMIT_REFRESH.

🐰 A rabbit hops through code so tight,
With types and limits shining bright,
From OAuth to tokens, each call now clear,
The strictest mypy brings no fear,
Type safety: the fluffy way! 🐇✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main changes: audit cleanup and mypy strict pass, which aligns with the substantial refactoring across typing, configuration, and bug fixes throughout the codebase.
Description check ✅ Passed The PR description covers the summary, changes, and test plan comprehensively, including rate limiting, UUID consistency fixes, LoginResponse refactoring, versioning changes, and mypy strict compliance. All template sections are addressed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/audit-cleanup-v0.9.2

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/test_auth.py (1)

910-915: ⚡ Quick win

Assert login success before reading refresh_token in the new test.

Adding a status assertion here makes failures explicit and easier to diagnose.

Suggested fix
         r = await client.post(
             "/api/v1/auth/login",
             json={"email": "rl@t.com", "password": "securepass123"},
         )
+        assert r.status_code == 200
         refresh_token = r.json()["refresh_token"]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_auth.py` around lines 910 - 915, The test reads
r.json()["refresh_token"] without asserting the login response succeeded; update
the test around the client.post("/api/v1/auth/login", json=...) call so you
first assert the response status (e.g., assert r.status_code == 200 or assert
r.ok) before accessing refresh_token, then extract refresh_token from r.json();
reference the existing variables r and refresh_token to locate the change.
pyproject.toml (1)

108-124: 🏗️ Heavy lift

Document the necessity of each disabled mypy code, or narrow to only those truly required by SQLAlchemy/SQLModel stubs.

The override disables 10 error codes for the adapter modules. While the commit message acknowledges SQLAlchemy 2.0 / SQLModel stub limitations, the scope includes codes like no-untyped-def, no-untyped-call, and no-any-return that appear tied to general adapter code patterns (untyped user parameters in _to_schema(), dynamic attribute access, nullable query results) rather than stub-specific issues alone. Consider documenting in a comment which specific stub limitation each code addresses, or refactoring the adapter code itself to minimize suppressions and preserve strict guarantees in these critical modules.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pyproject.toml` around lines 108 - 124, The mypy override for the adapter
modules currently disables many error codes; either annotate why each is
disabled with inline comments referencing the specific stub limitation or reduce
the list to only the truly required codes by fixing/adapting the adapter code:
add precise typing to functions like _to_schema(), annotate dynamic attribute
access with getattr casts, narrow nullable query results with
Optional/typing.cast or explicit checks, and replace untyped parameters/returns
with typed signatures or overloads so you can remove broad flags like
"no-untyped-def", "no-untyped-call", and "no-any-return"; document remaining
disabled codes next to the module entries indicating which SQLAlchemy/SQLModel
stub limitation or specific adapter function (e.g.,
fastapi_fullauth.adapters.sqlmodel.adapter._to_schema) necessitates each
suppression.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@fastapi_fullauth/__init__.py`:
- Line 1: Update the package version constant __version__ in
fastapi_fullauth/__init__.py from "0.9.2" to the intended release "0.10.0" so
dynamic versioning and builds reflect the correct release target.

In `@fastapi_fullauth/flows/passkey.py`:
- Around line 149-151: The current conversion of stored transport strings in
pk.transports to AuthenticatorTransport enums (the list comprehension building
transports=([AuthenticatorTransport(t) for t in pk.transports] if pk.transports
else None)) can raise ValueError for unknown values; update this to first filter
pk.transports to only known/valid transport values (e.g., compare each t against
the set of valid values from the AuthenticatorTransport enum or wrap individual
conversions in a try/except), then construct the list of AuthenticatorTransport
instances from the filtered values so begin_authentication won’t fail on invalid
persisted strings.

---

Nitpick comments:
In `@pyproject.toml`:
- Around line 108-124: The mypy override for the adapter modules currently
disables many error codes; either annotate why each is disabled with inline
comments referencing the specific stub limitation or reduce the list to only the
truly required codes by fixing/adapting the adapter code: add precise typing to
functions like _to_schema(), annotate dynamic attribute access with getattr
casts, narrow nullable query results with Optional/typing.cast or explicit
checks, and replace untyped parameters/returns with typed signatures or
overloads so you can remove broad flags like "no-untyped-def",
"no-untyped-call", and "no-any-return"; document remaining disabled codes next
to the module entries indicating which SQLAlchemy/SQLModel stub limitation or
specific adapter function (e.g.,
fastapi_fullauth.adapters.sqlmodel.adapter._to_schema) necessitates each
suppression.

In `@tests/test_auth.py`:
- Around line 910-915: The test reads r.json()["refresh_token"] without
asserting the login response succeeded; update the test around the
client.post("/api/v1/auth/login", json=...) call so you first assert the
response status (e.g., assert r.status_code == 200 or assert r.ok) before
accessing refresh_token, then extract refresh_token from r.json(); reference the
existing variables r and refresh_token to locate the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0a46a278-50e7-4822-b1c6-007e92a65a45

📥 Commits

Reviewing files that changed from the base of the PR and between de21af7 and 2d0b023.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (39)
  • .github/workflows/ci.yml
  • CHANGELOG.md
  • fastapi_fullauth/.agents/skills/fastapi-fullauth/references/api-reference.md
  • fastapi_fullauth/__init__.py
  • fastapi_fullauth/adapters/__init__.py
  • fastapi_fullauth/adapters/sqlalchemy/models/__init__.py
  • fastapi_fullauth/adapters/sqlmodel/models/__init__.py
  • fastapi_fullauth/backends/cookie.py
  • fastapi_fullauth/config.py
  • fastapi_fullauth/core/challenges.py
  • fastapi_fullauth/core/crypto.py
  • fastapi_fullauth/core/redis_blacklist.py
  • fastapi_fullauth/core/tokens.py
  • fastapi_fullauth/dependencies/current_user.py
  • fastapi_fullauth/dependencies/require_role.py
  • fastapi_fullauth/flows/change_password.py
  • fastapi_fullauth/flows/login.py
  • fastapi_fullauth/flows/oauth.py
  • fastapi_fullauth/flows/passkey.py
  • fastapi_fullauth/flows/password_reset.py
  • fastapi_fullauth/flows/register.py
  • fastapi_fullauth/flows/set_password.py
  • fastapi_fullauth/fullauth.py
  • fastapi_fullauth/middleware/csrf.py
  • fastapi_fullauth/middleware/security_headers.py
  • fastapi_fullauth/oauth/base.py
  • fastapi_fullauth/oauth/github.py
  • fastapi_fullauth/oauth/google.py
  • fastapi_fullauth/protection/lockout.py
  • fastapi_fullauth/protection/ratelimit.py
  • fastapi_fullauth/router/_models.py
  • fastapi_fullauth/router/admin.py
  • fastapi_fullauth/router/auth.py
  • fastapi_fullauth/router/oauth.py
  • fastapi_fullauth/router/passkey.py
  • fastapi_fullauth/router/profile.py
  • fastapi_fullauth/utils.py
  • pyproject.toml
  • tests/test_auth.py

@@ -1,4 +1,4 @@
__version__ = "0.9.1"
__version__ = "0.9.2"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align package version with the intended release target.

Given dynamic versioning now reads from this file, keeping 0.9.2 will stamp builds as 0.9.2. PR objectives indicate this should roll into 0.10.0, so this should be updated before merge to avoid accidental mis-versioned publish.

Suggested fix
-__version__ = "0.9.2"
+__version__ = "0.10.0"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
__version__ = "0.9.2"
__version__ = "0.10.0"
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fastapi_fullauth/__init__.py` at line 1, Update the package version constant
__version__ in fastapi_fullauth/__init__.py from "0.9.2" to the intended release
"0.10.0" so dynamic versioning and builds reflect the correct release target.

Comment on lines +149 to +151
transports=(
[AuthenticatorTransport(t) for t in pk.transports] if pk.transports else None
),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify where transports are written and where enum conversion occurs.
rg -n -C3 'transports' fastapi_fullauth/flows/passkey.py

Repository: mdfarhankc/fastapi-fullauth

Length of output: 1008


🏁 Script executed:

# Find PasskeyCredential model definition and its transports field type
rg -n -A5 "class PasskeyCredential" fastapi_fullauth/

Repository: mdfarhankc/fastapi-fullauth

Length of output: 385


🏁 Script executed:

# Check imports and understand AuthenticatorTransport enum
rg -n "AuthenticatorTransport\|from webauthn" fastapi_fullauth/flows/passkey.py

Repository: mdfarhankc/fastapi-fullauth

Length of output: 53


🏁 Script executed:

# Look for any validation or type hints on transports field
rg -n -B2 -A2 "transports.*:" fastapi_fullauth/

Repository: mdfarhankc/fastapi-fullauth

Length of output: 1849


🏁 Script executed:

# Check imports and find where AuthenticatorTransport comes from
rg -n "from webauthn" fastapi_fullauth/flows/passkey.py | head -20

Repository: mdfarhankc/fastapi-fullauth

Length of output: 486


🏁 Script executed:

# Search for any validation of transports during credential creation
rg -n -B5 -A5 "def create_passkey\|def register_passkey" fastapi_fullauth/

Repository: mdfarhankc/fastapi-fullauth

Length of output: 53


🏁 Script executed:

# Look at the registration flow to see if webauthn validates transports
rg -n -B3 -A8 "credential_id.*=" fastapi_fullauth/flows/passkey.py | head -40

Repository: mdfarhankc/fastapi-fullauth

Length of output: 1198


🌐 Web query:

webauthn python library AuthenticatorTransport enum valid values

💡 Result:

The most popular and actively maintained Python WebAuthn library is py_webauthn (also published as the 'webauthn' package on PyPI) from Duo Labs. Its AuthenticatorTransport enum, defined in webauthn/helpers/structs.py, has the following valid values (as lowercase strings): "usb", "nfc", "ble", "smart-card", "internal", "cable", "hybrid" [1][2][3][4]. Other libraries have slightly different sets: - webauthn-rp: "usb", "nfc", "ble", "internal" [5]. - python-fido2: Defines AuthenticatorTransport as a string enum, but specific values not listed in docs; accepts list of strings [6][7]. These match the WebAuthn spec transports, with py_webauthn including the most complete set per recent spec drafts (e.g., smart-card, hybrid added later) [1][8]. Use py_webauthn for comprehensive support.

Citations:


🏁 Script executed:

# Try to understand what values AuthenticatorTransport accepts
# First, let's see if there's any documentation or type hints in the codebase
rg -n "AuthenticatorTransport" fastapi_fullauth/ --type py

Repository: mdfarhankc/fastapi-fullauth

Length of output: 340


🏁 Script executed:

# Check if there's any version pinning or requirements file for webauthn
find . -name "requirements*.txt" -o -name "pyproject.toml" -o -name "poetry.lock" -o -name "setup.py" | head -5

Repository: mdfarhankc/fastapi-fullauth

Length of output: 88


🏁 Script executed:

# Check webauthn dependency and version
cat pyproject.toml | grep -A20 -B2 "webauthn\|dependencies"

Repository: mdfarhankc/fastapi-fullauth

Length of output: 1634


🏁 Script executed:

# Verify if transports are validated during the registration flow
# Look at the credential["response"].get("transports", []) and how it's handled
rg -n -B10 -A10 'credential\["response"\]\.get\("transports"' fastapi_fullauth/flows/passkey.py

Repository: mdfarhankc/fastapi-fullauth

Length of output: 955


Guard invalid WebAuthn transport values before enum conversion.

Line 150 converts stored transport strings to AuthenticatorTransport enum without validation. If any persisted transport value is invalid or unsupported (due to data migration, schema changes, or library updates), this raises ValueError and breaks begin_authentication for that user. Filter unknown values before enum construction.

🔧 Proposed fix
     from webauthn import generate_authentication_options, options_to_json
     from webauthn.helpers.structs import AuthenticatorTransport, PublicKeyCredentialDescriptor

+    valid_transports = {t.value for t in AuthenticatorTransport}
+
     allow_credentials = None
     if user_id is not None and adapter is not None:
         existing = await adapter.get_user_passkeys(user_id)
         allow_credentials = [
             PublicKeyCredentialDescriptor(
                 id=_b64_decode(pk.credential_id),
                 transports=(
-                    [AuthenticatorTransport(t) for t in pk.transports] if pk.transports else None
+                    [AuthenticatorTransport(t) for t in pk.transports if t in valid_transports]
+                    if pk.transports
+                    else None
                 ),
             )
             for pk in existing
         ]
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@fastapi_fullauth/flows/passkey.py` around lines 149 - 151, The current
conversion of stored transport strings in pk.transports to
AuthenticatorTransport enums (the list comprehension building
transports=([AuthenticatorTransport(t) for t in pk.transports] if pk.transports
else None)) can raise ValueError for unknown values; update this to first filter
pk.transports to only known/valid transport values (e.g., compare each t against
the set of valid values from the AuthenticatorTransport enum or wrap individual
conversions in a try/except), then construct the list of AuthenticatorTransport
instances from the filtered values so begin_authentication won’t fail on invalid
persisted strings.

@mdfarhankc mdfarhankc merged commit 25e02f9 into main May 14, 2026
7 checks passed
@mdfarhankc mdfarhankc deleted the fix/audit-cleanup-v0.9.2 branch May 14, 2026 18:21
@coderabbitai coderabbitai Bot mentioned this pull request May 20, 2026
3 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant