fix(oidc): raise callback code/state max_length from 512 to 2048 (#1024)

Facebook and some other OAuth providers issue authorization codes that
exceed 512 characters. Pydantic rejected these with 422 string_too_long.
The OAuth spec defines no maximum code length; 2048 aligns with common
provider limits.

Also adds three integration tests to verify 512-char and 2048-char codes
are accepted while 2049-char codes are correctly rejected.
This commit is contained in:
Sn0rrii
2026-04-19 08:10:56 +02:00
committed by GitHub
parent 68920f8c62
commit e958b10f75
2 changed files with 41 additions and 8 deletions
+2 -2
View File
@@ -1189,8 +1189,8 @@ async def oidc_authorize(
@router.get("/oidc/callback")
async def oidc_callback(
code: str | None = Query(default=None, max_length=512),
state: str | None = Query(default=None, max_length=512),
code: str | None = Query(default=None, max_length=2048),
state: str | None = Query(default=None, max_length=2048),
error: str | None = Query(default=None, max_length=256),
db: AsyncSession = Depends(get_db),
) -> RedirectResponse:
+39 -6
View File
@@ -3030,9 +3030,7 @@ class TestOIDCIssuerUrlTrailingSlash:
@pytest.mark.asyncio
@pytest.mark.integration
async def test_trailing_slash_issuer_url_fetches_correct_discovery_url(
self, async_client: AsyncClient
):
async def test_trailing_slash_issuer_url_fetches_correct_discovery_url(self, async_client: AsyncClient):
from unittest.mock import AsyncMock, MagicMock, patch
issuer_with_slash = "https://authentik.example.com/application/o/bambuddy/"
@@ -3082,9 +3080,7 @@ class TestOIDCIssuerUrlTrailingSlash:
@pytest.mark.asyncio
@pytest.mark.integration
async def test_iss_claim_trailing_slash_accepted(
self, async_client: AsyncClient, db_session: AsyncSession
):
async def test_iss_claim_trailing_slash_accepted(self, async_client: AsyncClient, db_session: AsyncSession):
"""Provider configured without trailing slash, Authentik JWT iss has trailing slash.
Both sides must be normalised before comparison so the login succeeds.
@@ -3197,3 +3193,40 @@ class TestOIDCIssuerUrlTrailingSlash:
"Trailing slash mismatch in iss claim must not cause token_validation_failed"
)
assert "oidc_token=" in location, f"Expected oidc_token in redirect, got: {location}"
class TestOIDCCallbackCodeLength:
"""OIDC callback code query param must accept up to 2048 characters (OAuth spec)."""
@pytest.mark.asyncio
@pytest.mark.integration
async def test_code_512_chars_accepted(self, async_client: AsyncClient):
"""A 512-character code (old limit) must not be rejected with 422."""
code = "a" * 512
resp = await async_client.get(
f"/api/v1/auth/oidc/callback?code={code}&state=bogus-state",
follow_redirects=False,
)
assert resp.status_code != 422, "512-char code must not be rejected by Pydantic"
@pytest.mark.asyncio
@pytest.mark.integration
async def test_code_2048_chars_accepted(self, async_client: AsyncClient):
"""A 2048-character code must not be rejected with 422."""
code = "a" * 2048
resp = await async_client.get(
f"/api/v1/auth/oidc/callback?code={code}&state=bogus-state",
follow_redirects=False,
)
assert resp.status_code != 422, "2048-char code must not be rejected by Pydantic"
@pytest.mark.asyncio
@pytest.mark.integration
async def test_code_2049_chars_rejected(self, async_client: AsyncClient):
"""A 2049-character code must be rejected with 422."""
code = "a" * 2049
resp = await async_client.get(
f"/api/v1/auth/oidc/callback?code={code}&state=bogus-state",
follow_redirects=False,
)
assert resp.status_code == 422, "2049-char code must be rejected by Pydantic"