heimdall icon indicating copy to clipboard operation
heimdall copied to clipboard

wip: Support authenticating multiple principals in a single request

Open dadrus opened this issue 3 months ago • 1 comments

Related issue(s)

closes #921 replaces #1317

Checklist

  • [x] I agree to follow this project's Code of Conduct.
  • [x] I have read, and I am following this repository's Contributing Guidelines.
  • [x] I have read the Security Policy.
  • [x] I have referenced an issue describing the bug/feature request.
  • [ ] I have added tests that prove the correctness of my implementation.
  • [ ] I have updated the documentation.

Description

Certain use cases, e.g. like described in the referenced FR #921 require information from multiple independent identity sources (e.g., user identity + client identity, or user identity + workload identity) to be available within the same request context. Previously, heimdall’s authenticators were evaluated in a sequential fallback manner — subsequent authenticators were only invoked if the preceding ones failed.

This PR continues the work done in #1487, #2260, #2665, #2825 and #2827, and extends heimdall’s authentication capabilities to support multiple principals within a single request. Authenticators can now be combined to produce distinct Principal objects, enabling the definition of multi-principal subjects. As a result, multiple authenticators can execute successfully and contribute separate identity data to the request’s Subject object, allowing policies and authorization decisions to leverage richer contextual information.

Behaviour

  • When multiple authenticators are configured with different principals, they will all be executed, and each successful authenticator contributes to the resulting subject.
  • When multiple authenticators are configured with the same principal, the evaluation follows the previous fallback behavior (the first successful authenticator wins).
  • The overall evaluation follows a conjunctive (CNF-like) composition model (see examples below)

Configuration and Access Model

Each authenticator step in the pipeline can now define an optional principal property that specifies the name of the principal it produces:

- authenticator: A
  principal: user
- authenticator: B
  principal: client

If the principal property is omitted, the authenticator writes to the "default" Principal:

- authenticator: A
- authenticator: B

The default principal is mandatory and is always expected to be created.

Subsequent pipeline stages can access principal data via the following syntax:

  • Subject.ID and Subject.Attributes → refer to Subject.default.ID and Subject.default.Attributes (syntactic sugar).
  • Subject.<principal-name>.ID and Subject.<principal-name>.Attributes → access explicitly named principals.

Examples

  1. Independent authenticators (A and B):

    - authenticator: A
      principal: default
    - authenticator: B
      principal: b
    

    Both A and B are executed; both principals default and b are established. If some of the authenticator steps fails, the entire authenticate stage fails.

  2. Fallback authenticators (A or B):

    - authenticator: A
    - authenticator: B
    

    Both write to the default principal; B acts as fallback if A fails. It basically defines the behavior which was available before this PR.

  3. Combined example (A and (B or C)):

    - authenticator: A
    - authenticator: B
      principal: b
    - authenticator: C
      principal: b
    

    A must succeed (creates default principal), and either B or C must succeed (creates b principal).

dadrus avatar Nov 04 '25 17:11 dadrus

Codecov Report

:x: Patch coverage is 88.66750% with 91 lines in your changes missing coverage. Please review. :white_check_mark: Project coverage is 90.86%. Comparing base (afd5552) to head (1e7fccb). :white_check_mark: All tests successful. No failed tests found.

Files with missing lines Patch % Lines
internal/rules/mechanisms/repository/repository.go 77.94% 10 Missing and 5 partials :warning:
internal/rules/converter/converter.go 42.85% 8 Missing and 4 partials :warning:
...chanisms/authenticators/anonymous_authenticator.go 87.50% 4 Missing :warning:
internal/rules/mechanisms/registry/registry.go 80.00% 2 Missing and 2 partials :warning:
internal/rules/rule_factory_impl.go 97.20% 3 Missing and 1 partial :warning:
internal/rules/composite_error_handler.go 50.00% 3 Missing :warning:
...hanisms/authenticators/basic_auth_authenticator.go 86.95% 3 Missing :warning:
.../rules/mechanisms/authorizers/remote_authorizer.go 84.21% 2 Missing and 1 partial :warning:
...chanisms/contextualizers/generic_contextualizer.go 81.25% 2 Missing and 1 partial :warning:
...s/mechanisms/contextualizers/map_contextualizer.go 80.00% 2 Missing and 1 partial :warning:
... and 21 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2849      +/-   ##
==========================================
- Coverage   91.21%   90.86%   -0.35%     
==========================================
  Files         298      295       -3     
  Lines       11196    11214      +18     
==========================================
- Hits        10212    10190      -22     
- Misses        715      757      +42     
+ Partials      269      267       -2     

:umbrella: View full report in Codecov by Sentry.
:loudspeaker: Have feedback on the report? Share it here.

codecov[bot] avatar Nov 04 '25 17:11 codecov[bot]