The "Request a new code" and "Try Again" links were outputting code_challenge=None when PKCE was not used, causing the retry to fail with validation errors. Now only includes PKCE parameters if code_challenge has a value. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
93 lines
3.9 KiB
Python
93 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Test script to verify client_id validation compliance with W3C IndieAuth spec Section 3.2."""
|
|
|
|
import sys
|
|
from urllib.parse import urlparse
|
|
|
|
# Import the validation function
|
|
sys.path.insert(0, '/home/phil/Projects/Gondulf/src')
|
|
from gondulf.utils.validation import normalize_client_id
|
|
|
|
|
|
def test_client_id(client_id: str, should_pass: bool, reason: str):
|
|
"""Test a single client_id against the spec."""
|
|
try:
|
|
result = normalize_client_id(client_id)
|
|
passed = True
|
|
print(f"✓ Accepted: {client_id} -> {result}")
|
|
except ValueError as e:
|
|
passed = False
|
|
print(f"✗ Rejected: {client_id} ({str(e)})")
|
|
|
|
if passed != should_pass:
|
|
print(f" ERROR: Expected {'pass' if should_pass else 'fail'}: {reason}")
|
|
return False
|
|
return True
|
|
|
|
|
|
def main():
|
|
"""Run comprehensive client_id validation tests."""
|
|
print("Testing client_id validation against W3C IndieAuth spec Section 3.2")
|
|
print("=" * 70)
|
|
|
|
tests = [
|
|
# Valid client_ids per spec
|
|
("https://example.com", True, "Valid HTTPS URL with domain"),
|
|
("https://example.com/", True, "Valid HTTPS URL with path /"),
|
|
("https://example.com/app", True, "Valid HTTPS URL with path"),
|
|
("https://example.com/app/client", True, "Valid HTTPS URL with multiple path segments"),
|
|
("https://example.com?foo=bar", True, "Valid HTTPS URL with query string"),
|
|
("https://example.com:8080", True, "Valid HTTPS URL with port"),
|
|
("https://example.com:8080/app", True, "Valid HTTPS URL with port and path"),
|
|
("http://localhost", True, "Valid HTTP localhost (loopback allowed)"),
|
|
("http://localhost:3000", True, "Valid HTTP localhost with port"),
|
|
("http://127.0.0.1", True, "Valid HTTP IPv4 loopback"),
|
|
("http://127.0.0.1:3000", True, "Valid HTTP IPv4 loopback with port"),
|
|
("http://[::1]", True, "Valid HTTP IPv6 loopback"),
|
|
("http://[::1]:3000", True, "Valid HTTP IPv6 loopback with port"),
|
|
|
|
# Invalid client_ids per spec
|
|
("http://example.com", False, "HTTP not allowed for non-loopback"),
|
|
("https://example.com#fragment", False, "Fragment not allowed"),
|
|
("https://user:pass@example.com", False, "Username/password not allowed"),
|
|
("https://192.168.1.1", False, "IPv4 address not allowed (except 127.0.0.1)"),
|
|
("https://[2001:db8::1]", False, "IPv6 address not allowed (except ::1)"),
|
|
("https://10.0.0.1", False, "Private IPv4 not allowed"),
|
|
("https://172.16.0.1", False, "Private IPv4 not allowed"),
|
|
("example.com", False, "Missing scheme"),
|
|
("ftp://example.com", False, "Invalid scheme (not http/https)"),
|
|
("https://", False, "Missing hostname"),
|
|
("https://example.com/../secret", False, "Path with .. segments not allowed"),
|
|
("https://example.com/./secret", False, "Path with . segments not allowed"),
|
|
|
|
# Edge cases that spec might not explicitly cover
|
|
("HTTPS://EXAMPLE.COM", True, "Uppercase scheme/host should be normalized"),
|
|
("https://example.com:443", True, "Default HTTPS port should be normalized"),
|
|
("http://localhost:80", True, "Default HTTP port on localhost"),
|
|
("https://xn--e1afmkfd.xn--p1ai", True, "Internationalized domain names (IDN)"),
|
|
]
|
|
|
|
all_passed = True
|
|
pass_count = 0
|
|
fail_count = 0
|
|
|
|
for client_id, should_pass, reason in tests:
|
|
if test_client_id(client_id, should_pass, reason):
|
|
pass_count += 1
|
|
else:
|
|
fail_count += 1
|
|
all_passed = False
|
|
|
|
print("\n" + "=" * 70)
|
|
print(f"Results: {pass_count} passed, {fail_count} failed")
|
|
|
|
if all_passed:
|
|
print("\n✅ All tests passed! client_id validation appears spec-compliant.")
|
|
else:
|
|
print("\n❌ Some tests failed. Review the validation logic against the spec.")
|
|
|
|
return 0 if all_passed else 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main()) |