From df9218f710514c1d2a7b0afe85e1bb3e8a3c9fd4 Mon Sep 17 00:00:00 2001 From: Corina Gum <14900841+corinagum@users.noreply.github.com> Date: Wed, 20 May 2026 14:27:25 -0700 Subject: [PATCH 1/2] JWT web token comments and tests --- .../api/auth/json_web_token.py | 18 ++++- .../tests/test_json_web_token_architecture.py | 73 +++++++++++++++++++ 2 files changed, 89 insertions(+), 2 deletions(-) create mode 100644 packages/api/tests/test_json_web_token_architecture.py diff --git a/packages/api/src/microsoft_teams/api/auth/json_web_token.py b/packages/api/src/microsoft_teams/api/auth/json_web_token.py index c5d9ac23..72f8df45 100644 --- a/packages/api/src/microsoft_teams/api/auth/json_web_token.py +++ b/packages/api/src/microsoft_teams/api/auth/json_web_token.py @@ -34,13 +34,27 @@ class JsonWebToken(TokenProtocol): def __init__(self, value: str): """ - Initialize JWT from token string. + Initialize a typed accessor over an already-validated JWT payload. + + This constructor performs no signature verification, no issuer/audience + checks, and no expiry enforcement. Constructing it from an untrusted + token does NOT establish trust in the contained claims. + + Signature verification happens at the HTTP trust boundary via + ``TokenValidator.validate_token`` (packages/apps/src/microsoft_teams/ + apps/auth/token_validator.py). Internal callers may also construct from + tokens sourced from trusted identity infrastructure (MSAL, Bot Framework + API responses). + + Callers must not construct this class from raw network input. Args: value: The JWT token string. """ self._value = value - # Decode without verification for payload extraction + # Decode without verification for payload extraction. + # Signature verification happens upstream in TokenValidator; see the + # class docstring for the trust-boundary contract. self._payload = JsonWebTokenPayload( **jwt.decode(value, algorithms=["RS256"], options={"verify_signature": False}) ) diff --git a/packages/api/tests/test_json_web_token_architecture.py b/packages/api/tests/test_json_web_token_architecture.py new file mode 100644 index 00000000..b55b9eba --- /dev/null +++ b/packages/api/tests/test_json_web_token_architecture.py @@ -0,0 +1,73 @@ +""" +Copyright (c) Microsoft Corporation. All rights reserved. +Licensed under the MIT License. + +Architectural test: locks the trust-boundary contract on JsonWebToken. + +JsonWebToken is a typed accessor over an *already-validated* JWT payload. +Constructing it does not verify the signature. Legitimate construction sites +must either run after the HTTP-boundary validator has executed, or wrap a +token sourced from trusted identity infrastructure (MSAL, Bot Framework API +responses). + +If a new file starts constructing JsonWebToken, this test fails so the +addition is reviewed against the trust-boundary contract. Add the file to +ALLOWLIST only after verifying it satisfies one of those conditions. +""" + +import re +from pathlib import Path + +# Path to the monorepo workspace root (teams.py). +REPO_ROOT = Path(__file__).resolve().parents[3] + +ALLOWLIST = { + # Token wrappers for MSAL / credentials acquisitions — trusted source. + "packages/apps/src/microsoft_teams/apps/token_manager.py", + # Wraps a token already validated by TokenValidator earlier in the + # request pipeline (http_server.py:109 invokes validate_token before + # the JsonWebToken construction at line 114). + "packages/apps/src/microsoft_teams/apps/http/http_server.py", + # Wraps user_token returned by an authenticated Teams API exchange. + "packages/apps/src/microsoft_teams/apps/routing/activity_context.py", +} + +# Match `JsonWebToken(` constructor calls. Excludes the class definition itself +# via the separate skip below. +CONSTRUCT_PATTERN = re.compile(r"\bJsonWebToken\s*\(") + +SKIP_DIRS = {".venv", "venv", "__pycache__", ".pytest_cache", "node_modules", ".git", "build", "dist"} +DEFINITION_FILE = "packages/api/src/microsoft_teams/api/auth/json_web_token.py" + + +def _iter_source_files() -> list[Path]: + sources: list[Path] = [] + for path in REPO_ROOT.rglob("*.py"): + if any(part in SKIP_DIRS for part in path.parts): + continue + # Tests, examples, and scaffolding scripts are not part of the + # shipped trust boundary surface. + if any(part in {"tests", "examples", "scripts", "templates"} for part in path.parts): + continue + sources.append(path) + return sources + + +def test_json_web_token_construction_is_allowlisted() -> None: + offenders: list[str] = [] + for path in _iter_source_files(): + relative = path.relative_to(REPO_ROOT).as_posix() + if relative == DEFINITION_FILE: + continue + contents = path.read_text(encoding="utf-8") + if not CONSTRUCT_PATTERN.search(contents): + continue + if relative not in ALLOWLIST: + offenders.append(relative) + + assert offenders == [], ( + "JsonWebToken construction found outside the allowlisted trust-boundary " + f"sites: {offenders}. If this is intentional, verify the new site runs " + "after a TokenValidator pass or wraps a token from trusted identity " + "infrastructure, then add it to ALLOWLIST in this test." + ) From e0c5d9d38933f5c8bb55dadc3ce559247814b93a Mon Sep 17 00:00:00 2001 From: Corina Gum <14900841+corinagum@users.noreply.github.com> Date: Fri, 22 May 2026 15:24:22 -0700 Subject: [PATCH 2/2] Drop architectural test; keep docstring only --- .../tests/test_json_web_token_architecture.py | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 packages/api/tests/test_json_web_token_architecture.py diff --git a/packages/api/tests/test_json_web_token_architecture.py b/packages/api/tests/test_json_web_token_architecture.py deleted file mode 100644 index b55b9eba..00000000 --- a/packages/api/tests/test_json_web_token_architecture.py +++ /dev/null @@ -1,73 +0,0 @@ -""" -Copyright (c) Microsoft Corporation. All rights reserved. -Licensed under the MIT License. - -Architectural test: locks the trust-boundary contract on JsonWebToken. - -JsonWebToken is a typed accessor over an *already-validated* JWT payload. -Constructing it does not verify the signature. Legitimate construction sites -must either run after the HTTP-boundary validator has executed, or wrap a -token sourced from trusted identity infrastructure (MSAL, Bot Framework API -responses). - -If a new file starts constructing JsonWebToken, this test fails so the -addition is reviewed against the trust-boundary contract. Add the file to -ALLOWLIST only after verifying it satisfies one of those conditions. -""" - -import re -from pathlib import Path - -# Path to the monorepo workspace root (teams.py). -REPO_ROOT = Path(__file__).resolve().parents[3] - -ALLOWLIST = { - # Token wrappers for MSAL / credentials acquisitions — trusted source. - "packages/apps/src/microsoft_teams/apps/token_manager.py", - # Wraps a token already validated by TokenValidator earlier in the - # request pipeline (http_server.py:109 invokes validate_token before - # the JsonWebToken construction at line 114). - "packages/apps/src/microsoft_teams/apps/http/http_server.py", - # Wraps user_token returned by an authenticated Teams API exchange. - "packages/apps/src/microsoft_teams/apps/routing/activity_context.py", -} - -# Match `JsonWebToken(` constructor calls. Excludes the class definition itself -# via the separate skip below. -CONSTRUCT_PATTERN = re.compile(r"\bJsonWebToken\s*\(") - -SKIP_DIRS = {".venv", "venv", "__pycache__", ".pytest_cache", "node_modules", ".git", "build", "dist"} -DEFINITION_FILE = "packages/api/src/microsoft_teams/api/auth/json_web_token.py" - - -def _iter_source_files() -> list[Path]: - sources: list[Path] = [] - for path in REPO_ROOT.rglob("*.py"): - if any(part in SKIP_DIRS for part in path.parts): - continue - # Tests, examples, and scaffolding scripts are not part of the - # shipped trust boundary surface. - if any(part in {"tests", "examples", "scripts", "templates"} for part in path.parts): - continue - sources.append(path) - return sources - - -def test_json_web_token_construction_is_allowlisted() -> None: - offenders: list[str] = [] - for path in _iter_source_files(): - relative = path.relative_to(REPO_ROOT).as_posix() - if relative == DEFINITION_FILE: - continue - contents = path.read_text(encoding="utf-8") - if not CONSTRUCT_PATTERN.search(contents): - continue - if relative not in ALLOWLIST: - offenders.append(relative) - - assert offenders == [], ( - "JsonWebToken construction found outside the allowlisted trust-boundary " - f"sites: {offenders}. If this is intentional, verify the new site runs " - "after a TokenValidator pass or wraps a token from trusted identity " - "infrastructure, then add it to ALLOWLIST in this test." - )