Skip to content

Commit ccdc0ba

Browse files
committed
SDK-8833. Moved org claim validation to helpers
1 parent 74d7e4d commit ccdc0ba

2 files changed

Lines changed: 40 additions & 35 deletions

File tree

src/auth0_server_python/auth_server/server_client.py

Lines changed: 2 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import asyncio
77
import json
88
import time
9-
import unicodedata
109
from collections import OrderedDict
1110
from typing import Any, Callable, Generic, Optional, TypeVar, Union
1211
from urllib.parse import parse_qs, urlencode, urlparse, urlunparse
@@ -63,6 +62,7 @@
6362
from auth0_server_python.utils import PKCE, URL, State
6463
from auth0_server_python.utils.helpers import (
6564
build_domain_resolver_context,
65+
validate_org_claims,
6666
validate_resolved_domain_value,
6767
)
6868

@@ -215,39 +215,7 @@ def _normalize_url(self, value: str) -> str:
215215
return value.rstrip('/')
216216

217217
def _validate_org_claims(self, claims: dict, expected_org: str) -> None:
218-
"""
219-
Validate org_id or org_name in token claims against the requested organization.
220-
221-
Uses expected_org prefix to determine which claim to check:
222-
- 'org_' prefix → validate claims['org_id'] exact match
223-
- no prefix → validate claims['org_name'] case-insensitive match
224-
225-
Raises:
226-
OrganizationTokenValidationError: if the claim is missing or mismatched.
227-
"""
228-
if expected_org.startswith("org_"):
229-
actual = claims.get("org_id")
230-
if not isinstance(actual, str):
231-
raise OrganizationTokenValidationError(
232-
"Organization Id (org_id) claim must be a string present in the ID token"
233-
)
234-
if actual != expected_org:
235-
raise OrganizationTokenValidationError(
236-
"Organization Id (org_id) claim value mismatch in the ID token"
237-
)
238-
else:
239-
actual = claims.get("org_name")
240-
if not isinstance(actual, str):
241-
raise OrganizationTokenValidationError(
242-
"Organization Name (org_name) claim must be a string present in the ID token"
243-
)
244-
# NFC-normalize before comparison: the same visual character (e.g. é) can have
245-
# multiple byte representations in Unicode. Normalizing both sides prevents
246-
# false rejections without risk of false matches.
247-
if unicodedata.normalize("NFC", actual).lower() != unicodedata.normalize("NFC", expected_org).lower():
248-
raise OrganizationTokenValidationError(
249-
"Organization Name (org_name) claim value mismatch in the ID token"
250-
)
218+
validate_org_claims(claims, expected_org)
251219

252220
async def _resolve_current_domain(self, store_options=None) -> str:
253221
"""Resolve domain from resolver function or return static domain."""

src/auth0_server_python/utils/helpers.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
import secrets
44
import string
55
import time
6+
import unicodedata
67
from typing import Any, Optional
78
from urllib.parse import parse_qs, urlencode, urlparse
89

910
from auth0_server_python.auth_types import DomainResolverContext
10-
from auth0_server_python.error import DomainResolverError
11+
from auth0_server_python.error import DomainResolverError, OrganizationTokenValidationError
1112

1213

1314
class PKCE:
@@ -293,3 +294,39 @@ def validate_resolved_domain_value(domain_value: Any) -> str:
293294
)
294295

295296
return domain_value
297+
298+
299+
def validate_org_claims(claims: dict, expected_org: str) -> None:
300+
"""
301+
Validate org_id or org_name in token claims against the requested organization.
302+
303+
Uses expected_org prefix to determine which claim to check:
304+
- 'org_' prefix → validate claims['org_id'] exact match (case-sensitive)
305+
- no prefix → validate claims['org_name'] case-insensitive match (NFC-normalized)
306+
307+
Raises:
308+
OrganizationTokenValidationError: if the claim is missing, not a string, or mismatched.
309+
"""
310+
if expected_org.startswith("org_"):
311+
actual = claims.get("org_id")
312+
if not isinstance(actual, str):
313+
raise OrganizationTokenValidationError(
314+
"Organization Id (org_id) claim must be a string present in the ID token"
315+
)
316+
if actual != expected_org:
317+
raise OrganizationTokenValidationError(
318+
"Organization Id (org_id) claim value mismatch in the ID token"
319+
)
320+
else:
321+
actual = claims.get("org_name")
322+
if not isinstance(actual, str):
323+
raise OrganizationTokenValidationError(
324+
"Organization Name (org_name) claim must be a string present in the ID token"
325+
)
326+
# NFC-normalize before comparison: the same visual character (e.g. é) can have
327+
# multiple byte representations in Unicode. Normalizing both sides prevents
328+
# false rejections without risk of false matches.
329+
if unicodedata.normalize("NFC", actual).lower() != unicodedata.normalize("NFC", expected_org).lower():
330+
raise OrganizationTokenValidationError(
331+
"Organization Name (org_name) claim value mismatch in the ID token"
332+
)

0 commit comments

Comments
 (0)