JWT token auth, MFA/TOTP, and security module suite#6
Open
ztane wants to merge 141 commits into
Open
Conversation
- Add pyjwt to the list of dependencies - Rename IUserAuthenticationService - Use includeme instead of auth_include - Add request to serect_callback
- Update docstrings. - Use config.action() to register directives, enabling conflict detection. - Improve variable names, headers, and other elements for clarity and consistency.
- Fix argument duplication in create_long_term_token call - config: Setup the ACLAuthorizationPolicy before AuthenticationPolicy
- Move it to the root level directory - Add pythonpath so it can identify the tests module itself - Add test_requires to the dependencies
- Ensure the fixture does not doing extra works such as asserting on every request and return the desired values instead.
- Make it possible to set the name of long_term_token, and access_token headers.
…missions, improve logging and exception handling.
- Implementing the secrect, and login callback(s) - Adding the route_prefix for callable module
- Adding more tests. - Update models.
- Update requires packages in setup.py - Update README - Update the return type for the mock callback - Meaningful name for the secret_callback
- Rename Ruff configuration - Reverse the Ruff formatting
- Increase the line length for Ruff and format it. - Add a dataclass for the default registered claims and include them in the declaratives. - Update the payload claims for JWT encoding.
- Add postgresql for testing - Update python matrix - Ensure datetime with UTC work in python 3.9+
- Add more tests for the app that applied JWTCookieAuthenticationPolicy - Add collection hooks to ignore test items if it does not match the required condition - Add JWTCookieAuthenticationPolicy - Automatically select the login view base on the security policy during the setup stage - Ability to set cookie name of the refresh token and access token
- Add refresh token endpoint - Set cookie for long term token only - Ability to set max age of the long term token - Bind the long term token cookie to specific route. e.g: /refresh route by default
MultiFactorAuthMethodType() enum constructor raises ValueError for invalid values, and all valid enum values are truthy, so the `if not mfa_method_type` branch was unreachable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ance zope Interface does not support Python's isinstance(); use INewAuthorizationPolicy.providedBy() which correctly checks whether an object implements the interface. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move json_body parsing after auth check in change_password - Add error handling to mfa_verify (HTTPException passthrough + generic) - Simplify mfa_method_type.value ternaries (enum is always truthy) - Use RFC 7518-compliant HMAC key length in test secret Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Use pyramid.authorization for Allow, Authenticated, Everyone, Deny (NO_PERMISSION_REQUIRED stays in pyramid.security — not moved yet) - Use 32+ byte wrong key in invalid signature test to avoid InsecureKeyLengthWarning Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
passlib.hash.sha256_crypt.encrypt() was deprecated in Passlib 1.7 in favor of .hash(). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…okens Same pattern as change_password: parse request body after auth check and inside try block so malformed requests still trigger audit events. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- tests/models/accounts.py: use sqlalchemy.orm.declarative_base - tet/sqlalchemy/password.py: use orm.declared_attr directly, remove redundant sqlalchemy.ext.declarative import Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move MFA check before token creation to prevent orphaned tokens - Use hmac.compare_digest for constant-time token comparison - Use dataclasses.replace to avoid mutating shared CookieAttributes - Read TOTP secret from DB instead of trusting client-provided setup_key - Fix verify_password to call user.validate_password (matching UserPasswordMixin) - Return HTTP 400 instead of None for unsupported MFA method types - Update tests to match new signatures and behaviors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… 2.0 compat - TOTP replay protection via UNLOGGED table + FOR UPDATE serialization - Rate limiting on login with separate DB connection (survives tx rollback) - Token cleanup for expired long-term tokens - Pyramid 2.0 compat module as single source of truth for moved imports - Public API contract tests (tests/test_public_api.py) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add RST stubs for auth, authentication, compat, config, events, mfa, models, policy, rate_limit, tokens, views - Fix duplicate object warnings with :no-index: on re-export modules - Fix docstring indentation for RST parsing in tokens.py and policy.py - Add authentication_apis guide to docs toctree Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add narrative getting-started documentation for tet.security covering setup, token flow, MFA, rate limiting, events, and configuration. Fix Sphinx autodoc warnings for pyramid_di reify_attr descriptors by adding an autodoc-process-signature hook that renders them as typed attributes instead of methods. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump version to 0.6a1 - Add CHANGES.md entry for the security module - Fix leeway field leaking into JWT payload (to_dict excluded it) - Remove phantom structlog dependency from [auth] extras - Fix qrcode[pil] -> qrcode (we use SVG, not PIL) - Add missing exports to tet.security.__init__ (TetRateLimitService, RateLimitAttemptMixin, TOTPUsedCodeMixin) - Remove Python 3.8/3.9 classifiers, add python_requires>=3.10 - Fix docs: method_type case (TOTP -> totp), change password field names (camelCase), MFA verify payload, list methods response key - Fix Union[TOTPData] -> TOTPData - Fix deprecated utcnow() in JWTRegisteredClaims docstring - Fix bcrypt -> passlib in security guide Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Bump version to 0.6a1 in pyproject.toml (replaces setup.py) - Add [auth] extras (pyjwt, pyotp, qrcode, requests) - Set python_requires>=3.10, update tool targets - Restore autodoc-process-signature hook for pyramid_di descriptors - Fix authorization tests: providedBy works, so wrapping happens Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tet.security modules require pyjwt and pyotp; expose them as a 'security' optional extra and have dev/test pull it in (CI installs .[dev]). Add the test-only deps the security suite needs: webtest, structlog, psycopg2-binary (tests run against PostgreSQL). Fixes 'No module named jwt' CI failures.
…nfigurable The security DB tests need pyramid_tm and zope.sqlalchemy (transaction-managed sessions) in addition to pyjwt/pyotp; add them to the security extra. Make the test DB URL overridable via TET_TEST_DB_URL (default unchanged: localhost:5432 for CI) so local runs don't collide with a host postgres on 5432.
Add 3.15-dev to the test matrix with allow-prereleases, marked continue-on-error so the unreleased nightly doesn't block the build.
# Conflicts: # .pre-commit-config.yaml # CHANGES.md
Removes the black dev dependency and [tool.black] config block. Formatting is handled by ruff-format. Claude-Session: https://claude.ai/code/session_011KKd5BtHMWfrF9WRRMwg7F
is_password_breached swallowed the RequestException and logged a bare "unavailable" line with no detail. Add exc_info=True so the actual error (timeout, DNS, HTTP status) is captured. Behaviour is unchanged: a failed check still degrades gracefully to False (see test_breach_api_timeout_graceful_degradation). Claude-Session: https://claude.ai/code/session_011KKd5BtHMWfrF9WRRMwg7F
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a batteries-included authentication/authorization stack under
tet.security, built onpyramid_diservices and SQLAlchemy. Rebased onto the currentsrc/tet/layoutmaster; the diff is clean (54 files, +6180/−123).What's included
tokens.py) — long-lived prefixed API tokens, generated withsecrets.token_bytes(32)and stored SHA-256 hashed at rest.authentication.py) — short-term JWTs with a pluggable signing secret via anISecretCallbackprotocol (no hardcoded secret).mfa.py) — enrolment + verification with replay protection.rate_limit.py) and token cleanup.events.py).policy.py,authorization.py) — wrapping fixed to useprovidedByoverisinstance.config.py,__init__.py), models (models.py), Pyramid 2.0 compat (compat.py), views (views.py: login, JWT issue/refresh, MFA, password, token revoke).Quality
tests/services/security/…, public-API contract tests).Known pre-merge TODOs (tracked in-branch)