CHANGELOG
=========

Version 0.5.0 (2026-08-09)
---------------------------

Security Fixes
~~~~~~~~~~~~~~~

- **Token expiration (``exp``) is now validated.** Previously,
  ``CognitoProvider``, ``IdentityPoolProvider``, and ``EntraIDProvider``
  only verified the JWT signature — a correctly-signed but *expired* token
  was still accepted, since ``joserfc.jwt.decode()`` does not check
  registered claims like ``exp``/``nbf``/``iat`` on its own. All three
  providers now reject tokens missing ``exp`` or past their expiration,
  via a new shared ``JWTProvider._validate_registered_claims()`` helper.
  A new ``jwt_leeway`` setting (default ``0``, seconds) is available on
  ``JWTProviderSettings`` (Cognito) and as
  ``AUTH_PROVIDER_AZURE_ENTRA_ID_LEEWAY`` (Entra ID) to tolerate clock
  skew between servers.

  **Action required:** if your tests use hand-crafted tokens without an
  ``exp`` claim, or your servers have significant clock drift from your
  identity provider, add ``exp``/configure ``jwt_leeway`` before
  upgrading.

- **Basic Auth passwords are now hashed with PBKDF2-HMAC-SHA256** (salted,
  600,000 iterations by default), via new ``hash_password()``/
  ``verify_password()`` functions exported from ``auth_middleware``.
  Previously ``BasicAuthMiddleware`` compared an unsalted raw SHA-256
  digest with a non-constant-time comparison. Existing credentials stored
  as unsalted SHA-256 hashes are still accepted for backward
  compatibility, but any new credentials should be created with
  ``hash_password()``.

- **``EntraIDProvider.get_keys()``/``get_openid_config()`` now handle
  errors properly.** Previously ``get_keys()`` had no error handling at
  all, and ``get_openid_config()`` silently swallowed any exception and
  returned ``{}``, which then surfaced as a confusing bare
  ``KeyError: 'jwks_uri'`` in ``load_jwks()``. Both now raise a clean
  ``InvalidTokenException`` (401) on HTTP errors, network errors, or
  malformed responses, matching ``CognitoProvider``'s behavior.

Breaking Changes
~~~~~~~~~~~~~~~~~

- **Removed the singleton pattern from ``CognitoProvider`` and
  ``EntraIDProvider``.** Previously, a second call to
  ``CognitoProvider(settings=...)`` (or ``EntraIDProvider(...)``) silently
  reused the *first* instance and ignored the new settings/providers —
  making it impossible to run two differently-configured instances (e.g.
  two Cognito user pools) in the same process. Each instantiation is now
  independent. ``Provider(settings=A) is Provider(settings=B)`` used to be
  ``True`` and is now ``False``.

- **``AUTH_MIDDLEWARE_DISABLED`` is now consistent across the whole
  library.** With it set, ``require_user()`` used to always raise 401
  (the opposite of "disabled"), and ``BasicAuthMiddleware`` used to always
  raise "No credentials provided" — both now let requests through with a
  synthetic user, consistent with ``JwtAuthMiddleware`` and
  ``require_groups``/``require_roles``/``require_permissions``, which
  already behaved this way.

- **``SqlGroupsProvider``/``SqlPermissionsProvider`` raise ``ValueError``
  instead of ``KeyError``** when a token is missing the identifier claim
  (previously hardcoded to ``"username"``, Cognito's convention). Both
  now accept a constructor argument ``id_claim`` (default ``"username"``,
  unchanged) so they can be used with Entra ID/OIDC tokens, which
  typically use ``"preferred_username"`` or ``"sub"`` instead.

New Features
~~~~~~~~~~~~

- **``OidcProvider``**: a new JWT provider for any standards-compliant
  OpenID Connect identity provider — Authentik, Keycloak, Auth0, Okta, or
  any issuer exposing a ``.well-known/openid-configuration`` discovery
  document. JWKS is discovered automatically (or configured explicitly),
  and ``iss``/``aud``/``exp``/``nbf`` are all validated. Replaces the
  previous "Generic JWT Provider" documentation page, which described
  configuration fields and direct instantiation of the abstract
  ``JWTProvider`` that never actually existed as working code.
  Documented in the new Sphinx pages ``oidc_provider.rst``,
  ``infrastructure/authentik-setup.rst``, and
  ``infrastructure/keycloak-setup.rst``.

Other Changes
~~~~~~~~~~~~~

- ``docs_source/jwt_auth_provider.rst`` renamed to ``docs_source/oidc_provider.rst``
  to match the real, working provider it now documents.

Version 0.4.6 (2026-08-09)
---------------------------

Security Fixes
~~~~~~~~~~~~~~~

- ``python-dotenv`` 1.2.1 → 1.2.2, fixing a symlink-following vulnerability
  in ``set_key()``/``unset_key()`` that could let a local attacker overwrite
  arbitrary files via a crafted symlink (CVE-2026-28684, GHSA-mf9w-mj56-hr94).
- ``requests`` 2.32.5 → 2.34.2 (dev-only, pulled in transitively by
  ``pysonar``), fixing insecure temp file reuse in
  ``extract_zipped_paths()`` (CVE-2026-25645, GHSA-gc5v-m9x4-r6x2). Not
  reachable through normal library usage; auth-middleware never calls
  ``extract_zipped_paths()`` directly.
- ``pysonar`` (dev dependency, used by ``make sonar``) bumped 1.4.0.4676 →
  1.7.0.5143 to relax its pin on ``requests`` (previously locked to
  exactly 2.32.5), which is what unblocked the ``requests`` fix above.

Version 0.4.5 (2026-08-09)
---------------------------

This release consolidates versions 0.4.2-0.4.5, which were published
without individual changelog entries.

Security Fixes
~~~~~~~~~~~~~~~

- **CognitoProvider.verify_token now validates the token's app client.**
  Previously only the JWT signature was checked against the user pool's
  JWKS; since Cognito signs tokens for every app client in a user pool
  with the same keys, any valid token from *any* app client of that pool
  was accepted. When ``user_pool_client_id`` is configured on
  ``CognitoAuthzProviderSettings``, the token's ``aud`` claim (ID tokens)
  or ``client_id`` claim (access tokens) is now checked against it after
  signature verification, and the token is rejected on mismatch. This
  check is optional and skipped when ``user_pool_client_id`` is not
  configured, for backward compatibility. ``IdentityPoolProvider``
  inherits this fix automatically since it extends ``CognitoProvider``.

  If you use ``CognitoProvider`` or ``IdentityPoolProvider``, set
  ``user_pool_client_id`` in ``CognitoAuthzProviderSettings`` to enable
  this check.

Breaking Changes
~~~~~~~~~~~~~~~~~

**Profile management removed.** The ``ProfileProvider`` contract
(``auth_middleware.contracts.profile_provider``) and
``CognitoProfileProvider``
(``auth_middleware.providers.aws.cognito_profile_provider``) have been
deleted, along with the related fields on ``User``, ``JWTProvider`` and
``CognitoProvider``. If you were using profile management, it is no
longer part of this library.

Dependency Updates
~~~~~~~~~~~~~~~~~~~

  PACKAGE               OLD        NEW
  --------------------   --------   --------
  joserfc                1.6.3      1.6.8
  pydantic-settings       2.12.0     2.14.2
  starlette               0.50.0     1.3.1
  fastapi (via starlette) 0.128.0    0.137.1
  cryptography            46.0.7     48.0.1
  urllib3                 2.6.3      2.7.0
  idna                    3.11       3.15
  tornado                 6.5.5      6.5.7
  mako                    1.3.10     1.3.12
  soupsieve               2.8.3      2.8.4

Other Changes
~~~~~~~~~~~~~

- Packaging/build workflow improvements (GitHub Actions release
  workflow, makefile, ``pyproject.toml``).
- README documentation improvements.

Version 0.4.1 (2026-05-02)
---------------------------

Breaking Changes
~~~~~~~~~~~~~~~~

**Guard helpers and checker classes** moved to a dedicated ``guards`` package.
The old top-level module paths have been **deleted** — they no longer exist.

  OLD LOCATION (deleted)                            NEW LOCATION
  -----------------------------------------------   -----------------------------------------------
  auth_middleware.functions                         auth_middleware.guards.functions
  auth_middleware.group_checker                     auth_middleware.guards.group_checker
  auth_middleware.role_checker                      auth_middleware.guards.role_checker
  auth_middleware.permissions_checker               auth_middleware.guards.permissions_checker

**Provider implementations** reorganised into technology-scoped packages.
All old module paths have been **deleted** — they no longer exist.

  OLD LOCATION (deleted)                                           NEW LOCATION
  --------------------------------------------------------------   ---------------------------------------------------
  auth_middleware.providers.authn.cognito_provider                 auth_middleware.providers.aws.cognito_provider
  auth_middleware.providers.authn.cognito_authz_provider_settings  auth_middleware.providers.aws.cognito_authz_provider_settings
  auth_middleware.providers.authn.identity_pool_provider           auth_middleware.providers.aws.identity_pool_provider
  auth_middleware.providers.authn.identity_pool_client             auth_middleware.providers.aws.identity_pool_client
  auth_middleware.providers.authn.identity_pool_settings           auth_middleware.providers.aws.identity_pool_settings
  auth_middleware.providers.authn.jwt_provider_settings            auth_middleware.providers.aws.jwt_provider_settings
  auth_middleware.providers.authz.cognito_groups_provider          auth_middleware.providers.aws.cognito_groups_provider
  auth_middleware.providers.authz.cognito_groups_as_roles_provider auth_middleware.providers.aws.cognito_groups_as_roles_provider
  auth_middleware.providers.profile.cognito_profile_provider       auth_middleware.providers.aws.cognito_profile_provider
  auth_middleware.providers.cognito (COGNITO_*_CLAIM constants)    auth_middleware.providers.aws (same constants)
  auth_middleware.providers.cognito.utils                          auth_middleware.providers.aws.utils
  auth_middleware.providers.exceptions.aws_exception               auth_middleware.providers.aws.aws_exception
  auth_middleware.providers.entra_id.entra_id_provider             auth_middleware.providers.azure.entra_id_provider
  auth_middleware.providers.entra_id.settings                      auth_middleware.providers.azure.settings
  auth_middleware.providers.entra_id.utils                         auth_middleware.providers.azure.utils
  auth_middleware.providers.exceptions.azure_exception             auth_middleware.providers.azure.azure_exception
  auth_middleware.providers.authz.async_database                   auth_middleware.providers.sqlalchemy.async_database
  auth_middleware.providers.authz.async_database_settings          auth_middleware.providers.sqlalchemy.async_database_settings
  auth_middleware.providers.authz.sql_base_model                   auth_middleware.providers.sqlalchemy.sql_base_model
  auth_middleware.providers.authz.sql_groups_provider              auth_middleware.providers.sqlalchemy.sql_groups_provider
  auth_middleware.providers.authz.sql_permissions_provider         auth_middleware.providers.sqlalchemy.sql_permissions_provider

Migration
~~~~~~~~~

Guards:

  # Before (no longer works — ImportError)
  from auth_middleware.functions import require_user, require_groups, require_roles
  from auth_middleware.functions import require_permissions, has_groups, has_roles
  from auth_middleware.functions import has_permissions, get_current_user
  from auth_middleware.group_checker import GroupChecker
  from auth_middleware.role_checker import RoleChecker
  from auth_middleware.permissions_checker import PermissionsChecker

  # After
  from auth_middleware.guards import (
      require_user,
      require_groups,
      require_roles,
      require_permissions,
      has_groups,
      has_roles,
      has_permissions,
      get_current_user,
      GroupChecker,
      RoleChecker,
      PermissionsChecker,
  )

Providers:

  providers.authn.*                        → providers.aws.*
  providers.authz.cognito_*                → providers.aws.cognito_*
  providers.authz.sql_*                    → providers.sqlalchemy.sql_*
  providers.authz.async_database*          → providers.sqlalchemy.async_database*
  providers.profile.*                      → providers.aws.*
  providers.cognito                        → providers.aws
  providers.entra_id.*                     → providers.azure.*
  providers.exceptions.aws_exception       → providers.aws.aws_exception
  providers.exceptions.azure_exception     → providers.azure.azure_exception

Deleted package directories: authn/, authz/, profile/, cognito/, entra_id/, exceptions/

Other Changes
~~~~~~~~~~~~~

- New internal module ``auth_middleware.constants`` introduced with
  ``AUTH_SCHEME_BASIC`` and ``AUTH_SCHEME_BEARER`` constants.
  This module is not part of the public API.

Version 0.4.0 (2026-05-01)
---------------------------

Breaking Changes
~~~~~~~~~~~~~~~~

Provider interfaces / ABCs moved to a dedicated ``contracts`` package.
The old module paths have been **deleted** — they no longer exist.

  OLD LOCATION (deleted)                                    NEW LOCATION
  -------------------------------------------------------  -----------------------------------------------
  auth_middleware.providers.authn.jwt_provider             auth_middleware.contracts.jwt_provider
  auth_middleware.providers.authz.groups_provider          auth_middleware.contracts.groups_provider
  auth_middleware.providers.authz.roles_provider           auth_middleware.contracts.roles_provider
  auth_middleware.providers.authz.permissions_provider     auth_middleware.contracts.permissions_provider
  auth_middleware.providers.profile.profile_provider       auth_middleware.contracts.profile_provider

Migration
~~~~~~~~~

Update your imports to use the new canonical paths:

  # Before (no longer works — ImportError)
  from auth_middleware.providers.authn.jwt_provider import JWTProvider
  from auth_middleware.providers.authz.groups_provider import GroupsProvider
  from auth_middleware.providers.authz.roles_provider import RolesProvider
  from auth_middleware.providers.authz.permissions_provider import PermissionsProvider
  from auth_middleware.providers.profile.profile_provider import ProfileProvider

  # After
  from auth_middleware.contracts import (
      JWTProvider,
      GroupsProvider,
      RolesProvider,
      PermissionsProvider,
      ProfileProvider,
  )

  # Or via top-level package
  from auth_middleware import JWTProvider, GroupsProvider, RolesProvider, PermissionsProvider, ProfileProvider

Other Changes
~~~~~~~~~~~~~

- Provider contracts are now exported from the top-level ``auth_middleware``
  package, making them available via ``from auth_middleware import JWTProvider``
  etc. without knowing the internal module structure.

Version 0.3.x and earlier
--------------------------

See git history for previous changes.
