Skip to content

feat: log OIDC failures through a configurable PSR-3 logger - #54

Open
turegjorup wants to merge 1 commit into
fix/cli-authenticator-preserves-causefrom
feature/psr3-logging
Open

feat: log OIDC failures through a configurable PSR-3 logger#54
turegjorup wants to merge 1 commit into
fix/cli-authenticator-preserves-causefrom
feature/psr3-logging

Conversation

@turegjorup

Copy link
Copy Markdown
Contributor

Summary

The bundle logged nothing at all — no LoggerInterface anywhere in src/. When the Azure client secret behind sites.itkdev.dk expired, the token exchange failed with invalid_client and the only visible symptom was a failed login; nothing recorded why.

Every failure path now logs its cause: state mismatch, empty/missing nonce, the token exchange and claims validation, unknown provider, unreachable provider, and the three CLI login token paths.

Logging is configurable via a new logging_options block:

itkdev_openid_connect:
  logging_options:
    logger: 'monolog.logger.openid_connect' # optional, defaults to the application logger
    level: error # optional, any PSR-3 level, defaults to error

Two design points worth review:

OpenIdLoginAuthenticator gets its logger via setLogger(), not the constructor. The class is abstract and subclassed by every consuming application, whose subclasses call parent::__construct($providerManager). A new constructor argument would either break them or silently leave the logger unset for all of them. It implements LoggerAwareInterface, and the extension applies setLogger()/setLogLevel() through registerForAutoconfiguration(), so consumer subclasses pick up the configuration with no wiring on their side.

LoggerAwareTrait is not used. Its $logger property is nullable, which forces a null check at all four call sites and leaves the class with two states where only one is meaningful; PHPStan also cannot narrow a mutable nullable property across methods. A private LoggerInterface $logger defaulted to NullLogger gives one code path — and removes four NullSafeMethodCall mutants that otherwise only existed because of the nullability.

Files Changed

  • src/Security/OpenIdLoginAuthenticator.php - implement LoggerAwareInterface with a NullLogger default; add setLogLevel(); log the four failure paths. onAuthenticationFailure() logs getPrevious() as the cause, because AuthenticatorManager::handleAuthenticationFailure() substitutes a generic BadCredentialsException for sensitive failures and keeps the real cause chained
  • src/Controller/LoginController.php, src/Security/CliLoginTokenAuthenticator.php - constructor-injected logger and level (container-instantiated, so no BC concern)
  • src/DependencyInjection/Configuration.php - logging_options.logger (service id, defaults to null) and logging_options.level (enum over the eight PSR-3 levels, defaults to error)
  • src/DependencyInjection/ItkDevOpenIdConnectExtension.php - wire both onto the bundle's own services and, via registerForAutoconfiguration(), onto consumer authenticator subclasses
  • src/Resources/config/services.yaml - monolog.logger channel tag (openid_connect) so consumers can route or alert on these logs; inert without MonologBundle
  • composer.json - add psr/log to require; it was only present transitively
  • tests/TestLogger.php - new in-memory PSR-3 spy; psr/log 3 ships no test double and every log call needs an assertion to survive Infection
  • README.md, CHANGELOG.md

Test Plan

  • task test:coverage — 106 tests, coverage 100% (9/9 classes, 30/30 methods, 319/319 lines)
  • task test:mutation — 147/147 mutants killed, Covered Code MSI 100% (threshold 95)
  • task analyze:php — PHPStan max level + custom exception-contract rules: no errors, no new ignores
  • task test:matrix — PHP 8.3/8.4/8.5 × prefer-lowest/prefer-stable: all 6 pass
  • task lint:php, lint:markdown, lint:yaml, composer validate --strict, composer normalize --dry-run — all clean

Every log assertion checks level, message, and context (including that the causing exception reaches context['exception']), and the success paths assert that nothing is logged.

Note on the base branch

Stacked on #53, which touches the same authenticator. Retarget to develop once that merges.

Nothing in the bundle logged anything, so an expired client secret surfaced
only as a failed login with no diagnostics. Every failure path now logs its
cause: state and nonce mismatches, the token exchange, unknown providers,
unreachable providers, and the CLI login token paths.

New logging_options config: `logger` selects the logger service (defaults to
the application logger) and `level` the PSR-3 level (defaults to error).
OpenIdLoginAuthenticator is abstract and subclassed by consumers, so it
receives both through registerForAutoconfiguration() rather than constructor
arguments that would break existing subclasses.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (34e22b3) to head (2ecace7).

Additional details and impacted files
@@                            Coverage Diff                            @@
##             fix/cli-authenticator-preserves-cause       #54   +/-   ##
=========================================================================
  Coverage                                   100.00%   100.00%           
- Complexity                                      62        69    +7     
=========================================================================
  Files                                            9         9           
  Lines                                          282       344   +62     
=========================================================================
+ Hits                                           282       344   +62     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants