From 281847b06fa013de66104a15acb986b81c98f8c7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 07:31:08 +0000 Subject: [PATCH 1/5] Initial plan From 57a1ca1831c60bac8f28336fbc7e2dcb754bd8b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 07:34:14 +0000 Subject: [PATCH 2/5] Fix AuthSession matching to ignore framework metadata (CorrelationId, Actor) Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Public/New-IdleAuthSessionBroker.ps1 | 37 ++++++++--- tests/Core/New-IdleAuthSession.Tests.ps1 | 62 +++++++++++++++++++ 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 b/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 index ba6c0a7b..e6f8b2d0 100644 --- a/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 +++ b/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 @@ -248,6 +248,25 @@ function New-IdleAuthSessionBroker { $authSessionNameMatches = @() $legacyMatches = @() + # Helper: Filter out framework-added metadata keys from Options + # CorrelationId and Actor are automatically added by the execution context + # and should not be considered when matching user-defined patterns. + $getUserOptions = { + param($opts) + if ($null -eq $opts -or $opts.Count -eq 0) { + return @{} + } + $filtered = @{} + foreach ($key in $opts.Keys) { + if ($key -notin @('CorrelationId', 'Actor')) { + $filtered[$key] = $opts[$key] + } + } + return $filtered + } + + $userOptions = & $getUserOptions $Options + foreach ($entry in $this.SessionMap.GetEnumerator()) { $pattern = $entry.Key @@ -260,23 +279,23 @@ function New-IdleAuthSessionBroker { # If pattern has ONLY AuthSessionName (no other keys) if ($pattern.Keys.Count -eq 1) { - # Only match if Options is null or empty - if ($null -eq $Options -or $Options.Count -eq 0) { + # Only match if user options (excluding framework metadata) is empty + if ($userOptions.Count -eq 0) { $authSessionNameMatches += $entry } continue } # Pattern has additional keys beyond AuthSessionName - # All other keys in pattern must match Options (if Options provided) + # All other keys in pattern must match user options (excluding framework metadata) $matches = $true foreach ($key in $pattern.Keys) { if ($key -eq 'AuthSessionName') { continue # Already checked } - # If Options is null or doesn't contain the key, no match - if ($null -eq $Options -or -not $Options.ContainsKey($key) -or $Options[$key] -ne $pattern[$key]) { + # If user options doesn't contain the key or value doesn't match, no match + if (-not $userOptions.ContainsKey($key) -or $userOptions[$key] -ne $pattern[$key]) { $matches = $false break } @@ -287,14 +306,14 @@ function New-IdleAuthSessionBroker { } } else { - # Legacy: pattern without AuthSessionName - match based on Options only - if ($null -eq $Options -or $Options.Count -eq 0) { - continue # No options to match + # Legacy: pattern without AuthSessionName - match based on user options only + if ($userOptions.Count -eq 0) { + continue # No user options to match } $matches = $true foreach ($key in $pattern.Keys) { - if (-not $Options.ContainsKey($key) -or $Options[$key] -ne $pattern[$key]) { + if (-not $userOptions.ContainsKey($key) -or $userOptions[$key] -ne $pattern[$key]) { $matches = $false break } diff --git a/tests/Core/New-IdleAuthSession.Tests.ps1 b/tests/Core/New-IdleAuthSession.Tests.ps1 index 06620ca5..8099f6bd 100644 --- a/tests/Core/New-IdleAuthSession.Tests.ps1 +++ b/tests/Core/New-IdleAuthSession.Tests.ps1 @@ -245,6 +245,68 @@ Describe 'New-IdleAuthSession' { } } + Context 'Framework metadata handling' { + It 'ignores CorrelationId when matching AuthSessionName-only pattern' { + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'Entra' } = @{ AuthSessionType = 'OAuth'; Credential = $testToken } + } + + # Framework adds CorrelationId and Actor to Options + $session = $broker.AcquireAuthSession('Entra', @{ CorrelationId = 'test-corr-id'; Actor = 'test-actor' }) + + $session | Should -Not -BeNullOrEmpty + $session | Should -Be 'mock-oauth-token-12345' + } + + It 'ignores Actor when matching AuthSessionName-only pattern' { + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD' } = @{ AuthSessionType = 'Credential'; Credential = $testCred } + } + + $session = $broker.AcquireAuthSession('AD', @{ Actor = 'admin-user' }) + + $session | Should -Not -BeNullOrEmpty + $session | Should -BeOfType [PSCredential] + $session.UserName | Should -Be 'TestUser' + } + + It 'ignores both CorrelationId and Actor when matching AuthSessionName-only pattern' { + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'EXO' } = @{ AuthSessionType = 'OAuth'; Credential = $testToken } + } + + $session = $broker.AcquireAuthSession('EXO', @{ CorrelationId = [guid]::NewGuid().ToString(); Actor = 'system' }) + + $session | Should -Not -BeNullOrEmpty + $session | Should -Be 'mock-oauth-token-12345' + } + + It 'matches AuthSessionName with additional user options despite framework metadata' { + $password1 = ConvertTo-SecureString 'Password1!' -AsPlainText -Force + $cred1 = New-Object System.Management.Automation.PSCredential('ADAdm', $password1) + + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD'; Role = 'Admin' } = $cred1 + } -AuthSessionType 'Credential' + + # Framework adds metadata, user provides Role + $session = $broker.AcquireAuthSession('AD', @{ Role = 'Admin'; CorrelationId = 'test-id'; Actor = 'user' }) + + $session | Should -Not -BeNullOrEmpty + $session.UserName | Should -Be 'ADAdm' + } + + It 'does not match when user provides non-matching options even with framework metadata' { + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD' } = @{ AuthSessionType = 'Credential'; Credential = $testCred } + } + + # User provides Role which is not in pattern, so should not match despite framework metadata + { $broker.AcquireAuthSession('AD', @{ Role = 'Admin'; CorrelationId = 'test' }) } | + Should -Throw '*No matching auth session found*' + } + } + Context 'Module export' { It 'is available as exported command from IdLE module' { $command = Get-Command -Name New-IdleAuthSession -ErrorAction SilentlyContinue From 92a7ad78a1b2514b5bf6ee0cbbd0297c6bc0df82 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:23:24 +0000 Subject: [PATCH 3/5] Fix framework metadata filtering to support Actor-based routing - Changed approach: only ignore framework metadata for AuthSessionName-only patterns - Multi-key patterns (including Actor/CorrelationId) now work for advanced routing - Added validation warnings when framework keys are used in patterns - Updated tests to cover Actor-based routing scenarios Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- .../Public/New-IdleAuthSessionBroker.ps1 | 80 ++++++++++++------- tests/Core/New-IdleAuthSession.Tests.ps1 | 44 ++++++++++ 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 b/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 index e6f8b2d0..8db50910 100644 --- a/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 +++ b/src/IdLE.Core/Public/New-IdleAuthSessionBroker.ps1 @@ -180,6 +180,31 @@ function New-IdleAuthSessionBroker { 'SessionMap' ) } + + # Validate that patterns don't use framework-reserved keys inappropriately. + # CorrelationId and Actor are automatically injected by the execution context. + # Using them in patterns can lead to unexpected behavior: + # - For AuthSessionName-only patterns, they are ignored (to fix the reported bug) + # - For multi-key patterns, they CAN be matched, but values are framework-controlled + # We warn about this to help users avoid confusion. + $frameworkKeys = @('CorrelationId', 'Actor') + $hasFrameworkKeys = $false + foreach ($fwKey in $frameworkKeys) { + if ($pattern.ContainsKey($fwKey)) { + $hasFrameworkKeys = $true + break + } + } + + if ($hasFrameworkKeys) { + # Issue a warning for patterns using framework-controlled keys. + # These CAN match (in multi-key patterns), but values are controlled by the framework. + # Users should be aware this may not work as expected since CorrelationId/Actor + # are automatically set per execution, not per workflow definition. + $patternDesc = ($pattern.Keys | ForEach-Object { "$_=$($pattern[$_])" }) -join ', ' + Write-Warning "SessionMap pattern { $patternDesc } includes framework-controlled keys (CorrelationId, Actor). These keys are automatically set by the execution context and may not match user expectations. Consider using user-defined routing keys instead." + } + # Create a readable pattern description for error messages $patternDesc = ($pattern.Keys | ForEach-Object { "$_=$($pattern[$_])" }) -join ', ' $context = "SessionMap entry { $patternDesc }" @@ -248,24 +273,10 @@ function New-IdleAuthSessionBroker { $authSessionNameMatches = @() $legacyMatches = @() - # Helper: Filter out framework-added metadata keys from Options - # CorrelationId and Actor are automatically added by the execution context - # and should not be considered when matching user-defined patterns. - $getUserOptions = { - param($opts) - if ($null -eq $opts -or $opts.Count -eq 0) { - return @{} - } - $filtered = @{} - foreach ($key in $opts.Keys) { - if ($key -notin @('CorrelationId', 'Actor')) { - $filtered[$key] = $opts[$key] - } - } - return $filtered - } - - $userOptions = & $getUserOptions $Options + # Framework metadata keys that are automatically injected by the execution context. + # These keys should be excluded when matching AuthSessionName-only patterns, + # but CAN be used in multi-key patterns for advanced routing scenarios. + $frameworkMetadataKeys = @('CorrelationId', 'Actor') foreach ($entry in $this.SessionMap.GetEnumerator()) { $pattern = $entry.Key @@ -279,23 +290,36 @@ function New-IdleAuthSessionBroker { # If pattern has ONLY AuthSessionName (no other keys) if ($pattern.Keys.Count -eq 1) { - # Only match if user options (excluding framework metadata) is empty - if ($userOptions.Count -eq 0) { + # For AuthSessionName-only patterns, ignore framework metadata in Options. + # This allows simple patterns like @{ AuthSessionName = 'Entra' } to match + # even when the framework injects CorrelationId and Actor. + $hasUserOptions = $false + if ($null -ne $Options -and $Options.Count -gt 0) { + foreach ($key in $Options.Keys) { + if ($key -notin $frameworkMetadataKeys) { + $hasUserOptions = $true + break + } + } + } + + if (-not $hasUserOptions) { $authSessionNameMatches += $entry } continue } - # Pattern has additional keys beyond AuthSessionName - # All other keys in pattern must match user options (excluding framework metadata) + # Pattern has additional keys beyond AuthSessionName. + # Match ALL keys in pattern against Options (including Actor/CorrelationId if present). + # This supports advanced routing: @{ AuthSessionName = 'AD'; Actor = 'ops-user' } $matches = $true foreach ($key in $pattern.Keys) { if ($key -eq 'AuthSessionName') { continue # Already checked } - # If user options doesn't contain the key or value doesn't match, no match - if (-not $userOptions.ContainsKey($key) -or $userOptions[$key] -ne $pattern[$key]) { + # If Options is null or doesn't contain the key, no match + if ($null -eq $Options -or -not $Options.ContainsKey($key) -or $Options[$key] -ne $pattern[$key]) { $matches = $false break } @@ -306,14 +330,14 @@ function New-IdleAuthSessionBroker { } } else { - # Legacy: pattern without AuthSessionName - match based on user options only - if ($userOptions.Count -eq 0) { - continue # No user options to match + # Legacy: pattern without AuthSessionName - match based on Options only + if ($null -eq $Options -or $Options.Count -eq 0) { + continue # No options to match } $matches = $true foreach ($key in $pattern.Keys) { - if (-not $userOptions.ContainsKey($key) -or $userOptions[$key] -ne $pattern[$key]) { + if (-not $Options.ContainsKey($key) -or $Options[$key] -ne $pattern[$key]) { $matches = $false break } diff --git a/tests/Core/New-IdleAuthSession.Tests.ps1 b/tests/Core/New-IdleAuthSession.Tests.ps1 index 8099f6bd..4aa79aa5 100644 --- a/tests/Core/New-IdleAuthSession.Tests.ps1 +++ b/tests/Core/New-IdleAuthSession.Tests.ps1 @@ -305,6 +305,50 @@ Describe 'New-IdleAuthSession' { { $broker.AcquireAuthSession('AD', @{ Role = 'Admin'; CorrelationId = 'test' }) } | Should -Throw '*No matching auth session found*' } + + It 'allows Actor-based routing in multi-key patterns' { + $password1 = ConvertTo-SecureString 'OpsPassword!' -AsPlainText -Force + $opsCred = New-Object System.Management.Automation.PSCredential('ops-user', $password1) + + $password2 = ConvertTo-SecureString 'AdminPassword!' -AsPlainText -Force + $adminCred = New-Object System.Management.Automation.PSCredential('admin-user', $password2) + + # Suppress warnings for this test since we're intentionally using framework keys + $WarningPreference = 'SilentlyContinue' + $broker = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD'; Actor = 'ops-user' } = $opsCred + @{ AuthSessionName = 'AD'; Actor = 'admin-user' } = $adminCred + } -AuthSessionType 'Credential' + $WarningPreference = 'Continue' + + # Match ops-user + $session = $broker.AcquireAuthSession('AD', @{ Actor = 'ops-user'; CorrelationId = 'test' }) + $session.UserName | Should -Be 'ops-user' + + # Match admin-user + $session2 = $broker.AcquireAuthSession('AD', @{ Actor = 'admin-user'; CorrelationId = 'test2' }) + $session2.UserName | Should -Be 'admin-user' + } + + It 'issues warning when patterns include framework keys' { + $warnings = @() + $null = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD'; Actor = 'test' } = @{ AuthSessionType = 'Credential'; Credential = $testCred } + } -WarningVariable warnings -WarningAction SilentlyContinue + + $warnings.Count | Should -BeGreaterThan 0 + $warnings[0] | Should -Match 'framework-controlled keys' + } + + It 'issues warning when multi-key pattern with non-framework keys also includes framework keys' { + $warnings = @() + $null = New-IdleAuthSession -SessionMap @{ + @{ AuthSessionName = 'AD'; Actor = 'test'; Role = 'Admin' } = @{ AuthSessionType = 'Credential'; Credential = $testCred } + } -WarningVariable warnings -WarningAction SilentlyContinue + + $warnings.Count | Should -BeGreaterThan 0 + $warnings[0] | Should -Match 'framework-controlled keys' + } } Context 'Module export' { From 097b6e53fcd627ab67ef35607a4351f57c97900b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:24:29 +0000 Subject: [PATCH 4/5] Add documentation for framework-reserved keys (CorrelationId, Actor) - Added note to authentication walkthrough explaining reserved key behavior - Added detailed documentation to New-IdleAuthSession cmdlet reference - Explains when framework keys are ignored vs. when they participate in matching - Recommends using user-defined keys to avoid confusion Co-authored-by: blindzero <13959569+blindzero@users.noreply.github.com> --- docs/reference/cmdlets/New-IdleAuthSession.md | 12 ++++++++++++ docs/use/walkthrough/05-providers-authentication.md | 10 ++++++++++ 2 files changed, 22 insertions(+) diff --git a/docs/reference/cmdlets/New-IdleAuthSession.md b/docs/reference/cmdlets/New-IdleAuthSession.md index c8a04cbe..447ca322 100644 --- a/docs/reference/cmdlets/New-IdleAuthSession.md +++ b/docs/reference/cmdlets/New-IdleAuthSession.md @@ -119,7 +119,19 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## OUTPUTS ### PSCustomObject with AcquireAuthSession method + ## NOTES + +### Framework-Reserved Keys + +The execution framework automatically injects `CorrelationId` and `Actor` into auth session options during plan execution. + +**Behavior:** +- **AuthSessionName-only patterns** (e.g., `@{ AuthSessionName = 'Entra' }`): Framework keys are automatically ignored during matching, so simple patterns work as expected +- **Multi-key patterns** (e.g., `@{ AuthSessionName = 'AD'; Actor = 'ops-user' }`): ALL keys including `Actor` and `CorrelationId` participate in matching, enabling actor-based routing + +**Warning:** Using `Actor` or `CorrelationId` in patterns will trigger a warning because these values are framework-controlled and change per execution. Consider using user-defined routing keys (like `Role`, `Environment`, `Tier`) instead for more predictable behavior. + For detailed documentation, see: Get-Help IdLE.Core\New-IdleAuthSessionBroker -Full ## RELATED LINKS diff --git a/docs/use/walkthrough/05-providers-authentication.md b/docs/use/walkthrough/05-providers-authentication.md index 97e96ae8..0ff7abd7 100644 --- a/docs/use/walkthrough/05-providers-authentication.md +++ b/docs/use/walkthrough/05-providers-authentication.md @@ -146,6 +146,16 @@ $broker = New-IdleAuthSession -SessionMap @{ } ``` +:::info Framework-Reserved Keys + +The execution framework automatically injects `CorrelationId` and `Actor` into auth session options during execution. These keys have special handling: + +- **AuthSessionName-only patterns** (e.g., `@{ AuthSessionName = 'AD' }`): Framework keys are ignored during matching, allowing simple patterns to work regardless of injected metadata +- **Multi-key patterns** (e.g., `@{ AuthSessionName = 'AD'; Actor = 'ops-user' }`): Framework keys participate in matching, enabling advanced actor-based routing + +**Recommendation**: Use user-defined routing keys (like `Role`, `Environment`, `Tier`) instead of `Actor` or `CorrelationId` to avoid confusion, as framework values change per execution and are not under user control. +::: + To make the broker available at runtime, add it to the provider registry under the key `AuthSessionBroker`: ```powershell From 7fb219d5641bd119fd0f2e83a75d3866c4f4809c Mon Sep 17 00:00:00 2001 From: Matthias Fleschuetz <55826276+ntt-matthias-fleschuetz@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:14:01 +0100 Subject: [PATCH 5/5] docs: updated New-IdleAuthSession reference --- docs/reference/cmdlets/New-IdleAuthSession.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/reference/cmdlets/New-IdleAuthSession.md b/docs/reference/cmdlets/New-IdleAuthSession.md index 447ca322..c8a04cbe 100644 --- a/docs/reference/cmdlets/New-IdleAuthSession.md +++ b/docs/reference/cmdlets/New-IdleAuthSession.md @@ -119,19 +119,7 @@ This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable ## OUTPUTS ### PSCustomObject with AcquireAuthSession method - ## NOTES - -### Framework-Reserved Keys - -The execution framework automatically injects `CorrelationId` and `Actor` into auth session options during plan execution. - -**Behavior:** -- **AuthSessionName-only patterns** (e.g., `@{ AuthSessionName = 'Entra' }`): Framework keys are automatically ignored during matching, so simple patterns work as expected -- **Multi-key patterns** (e.g., `@{ AuthSessionName = 'AD'; Actor = 'ops-user' }`): ALL keys including `Actor` and `CorrelationId` participate in matching, enabling actor-based routing - -**Warning:** Using `Actor` or `CorrelationId` in patterns will trigger a warning because these values are framework-controlled and change per execution. Consider using user-defined routing keys (like `Role`, `Environment`, `Tier`) instead for more predictable behavior. - For detailed documentation, see: Get-Help IdLE.Core\New-IdleAuthSessionBroker -Full ## RELATED LINKS