From 19e8733bd5b5ce7e7fce8c5617c9753188b82a16 Mon Sep 17 00:00:00 2001 From: johannesconsulting Date: Sun, 5 Oct 2025 15:46:28 +0200 Subject: [PATCH] Add FIDO2 authentication support and related tests - Implemented Invoke-FIDO2Authentication function for FIDO2 authentication flow. - Added ConvertFrom-Base64UrlString function for Base64Url decoding. - Created tests for New-PASSession and ConvertFrom-Base64UrlString. - Updated documentation for New-PASSession to include new parameters. - Added DSInternals.Win32.WebAuthn.dll and its license. --- Tests/ConvertFrom-Base64UrlString.Tests.ps1 | 94 +++++++++++ Tests/Invoke-FIDO2Authentication.Tests.ps1 | 128 ++++++++++++++ Tests/New-PASSession.Tests.ps1 | 63 +++++++ docs/collections/_commands/New-PASSession.md | 56 +++++-- .../Authentication/New-PASSession.ps1 | 28 +++- psPAS/Private/ConvertFrom-Base64UrlString.ps1 | 45 +++++ psPAS/Private/Invoke-FIDO2Authentication.ps1 | 156 ++++++++++++++++++ psPAS/en-US/psPAS-help.xml | 46 +++++- psPAS/lib/DSInternals.LICENSE | 21 +++ psPAS/lib/DSInternals.Win32.WebAuthn.dll | Bin 0 -> 102824 bytes psPAS/psPAS.psm1 | 3 + 11 files changed, 618 insertions(+), 22 deletions(-) create mode 100644 Tests/ConvertFrom-Base64UrlString.Tests.ps1 create mode 100644 Tests/Invoke-FIDO2Authentication.Tests.ps1 create mode 100644 psPAS/Private/ConvertFrom-Base64UrlString.ps1 create mode 100644 psPAS/Private/Invoke-FIDO2Authentication.ps1 create mode 100644 psPAS/lib/DSInternals.LICENSE create mode 100644 psPAS/lib/DSInternals.Win32.WebAuthn.dll diff --git a/Tests/ConvertFrom-Base64UrlString.Tests.ps1 b/Tests/ConvertFrom-Base64UrlString.Tests.ps1 new file mode 100644 index 00000000..4edf91e4 --- /dev/null +++ b/Tests/ConvertFrom-Base64UrlString.Tests.ps1 @@ -0,0 +1,94 @@ +Describe $($PSCommandPath -Replace '.Tests.ps1') { + + BeforeAll { + #Get Current Directory + $Here = Split-Path -Parent $PSCommandPath + + #Assume ModuleName from Repository Root folder + $ModuleName = Split-Path (Split-Path $Here -Parent) -Leaf + + #Resolve Path to Module Directory + $ModulePath = Resolve-Path "$Here\..\$ModuleName" + + #Define Path to Module Manifest + $ManifestPath = Join-Path "$ModulePath" "$ModuleName.psd1" + + if ( -not (Get-Module -Name $ModuleName -All)) { + + Import-Module -Name "$ManifestPath" -ArgumentList $true -Force -ErrorAction Stop + + } + + $Script:RequestBody = $null + $psPASSession = [ordered]@{ + BaseURI = 'https://SomeURL/SomeApp' + User = $null + ExternalVersion = [System.Version]'0.0' + WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + StartTime = $null + ElapsedTime = $null + LastCommand = $null + LastCommandTime = $null + LastCommandResults = $null + } + + New-Variable -Name psPASSession -Value $psPASSession -Scope Script -Force + + } + + + AfterAll { + + $Script:RequestBody = $null + + } + + InModuleScope $(Split-Path (Split-Path (Split-Path -Parent $PSCommandPath) -Parent) -Leaf ) { + + Context 'Mandatory Parameters' { + + $Parameters = @{Parameter = 'InputString' } + + It 'specifies parameter as mandatory' -TestCases $Parameters { + + param($Parameter) + + (Get-Command ConvertFrom-Base64UrlString).Parameters["$Parameter"].Attributes.Mandatory | Should -Be $true + + } + + } + + Context 'Base64Url Decoding' { + + It 'decodes Base64Url string without padding' { + $base64Url = 'SGVsbG8gV29ybGQ' + $result = ConvertFrom-Base64UrlString -InputString $base64Url + $resultString = [System.Text.Encoding]::UTF8.GetString($result) + $resultString | Should -Be 'Hello World' + } + + It 'decodes Base64Url with URL-safe characters (dash and underscore)' { + $base64Url = 'PDw_Pz8-Pg' + $result = ConvertFrom-Base64UrlString -InputString $base64Url + $result | Should -Not -BeNullOrEmpty + } + + It 'handles padding correctly' { + $base64Url = 'YWJj' + $result = ConvertFrom-Base64UrlString -InputString $base64Url + $resultString = [System.Text.Encoding]::UTF8.GetString($result) + $resultString | Should -Be 'abc' + } + + It 'converts Base64Url to byte array' { + $base64Url = 'VGVzdA' + $result = ConvertFrom-Base64UrlString -InputString $base64Url + $result.GetType().BaseType.Name | Should -Be 'Array' + } + + } + + } + +} diff --git a/Tests/Invoke-FIDO2Authentication.Tests.ps1 b/Tests/Invoke-FIDO2Authentication.Tests.ps1 new file mode 100644 index 00000000..db053025 --- /dev/null +++ b/Tests/Invoke-FIDO2Authentication.Tests.ps1 @@ -0,0 +1,128 @@ +Describe $($PSCommandPath -Replace '.Tests.ps1') { + + BeforeAll { + #Get Current Directory + $Here = Split-Path -Parent $PSCommandPath + + #Assume ModuleName from Repository Root folder + $ModuleName = Split-Path (Split-Path $Here -Parent) -Leaf + + #Resolve Path to Module Directory + $ModulePath = Resolve-Path "$Here\..\$ModuleName" + + #Define Path to Module Manifest + $ManifestPath = Join-Path "$ModulePath" "$ModuleName.psd1" + + if ( -not (Get-Module -Name $ModuleName -All)) { + + Import-Module -Name "$ManifestPath" -ArgumentList $true -Force -ErrorAction Stop + + } + + $Script:RequestBody = $null + $psPASSession = [ordered]@{ + BaseURI = 'https://SomeURL/SomeApp' + User = $null + ExternalVersion = [System.Version]'0.0' + WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + StartTime = $null + ElapsedTime = $null + LastCommand = $null + LastCommandTime = $null + LastCommandResults = $null + } + + New-Variable -Name psPASSession -Value $psPASSession -Scope Script -Force + + } + + + AfterAll { + + $Script:RequestBody = $null + + } + + InModuleScope $(Split-Path (Split-Path (Split-Path -Parent $PSCommandPath) -Parent) -Leaf ) { + + Context 'Mandatory Parameters' { + + $Parameters = @( + @{Parameter = 'BaseURI' }, + @{Parameter = 'UserName' } + ) + + It 'specifies parameter as mandatory' -TestCases $Parameters { + + param($Parameter) + + (Get-Command Invoke-FIDO2Authentication).Parameters["$Parameter"].Attributes.Mandatory | Should -Be $true + + } + + It 'specifies parameter LogonRequest as optional' { + + (Get-Command Invoke-FIDO2Authentication).Parameters["LogonRequest"].Attributes.Mandatory | Should -Be $false + + } + + } + + Context 'Platform Requirements' { + + BeforeEach { + $IsWindowsPlatform = (-not (Test-IsCoreCLR)) -or $IsWindows + } + + It 'requires Windows platform' { + if (-not $IsWindowsPlatform) { + { Invoke-FIDO2Authentication -BaseURI 'https://pvwa.example.com' -UserName 'testuser' -LogonRequest @{} } | Should -Throw '*Windows*' + } + } + + } + + Context 'Input Validation' { + + It 'accepts BaseURI parameter' { + $params = (Get-Command Invoke-FIDO2Authentication).Parameters['BaseURI'] + $params | Should -Not -BeNullOrEmpty + $params.ParameterType.Name | Should -Be 'String' + } + + It 'accepts UserName parameter' { + $params = (Get-Command Invoke-FIDO2Authentication).Parameters['UserName'] + $params | Should -Not -BeNullOrEmpty + $params.ParameterType.Name | Should -Be 'String' + } + + It 'accepts LogonRequest parameter' { + $params = (Get-Command Invoke-FIDO2Authentication).Parameters['LogonRequest'] + $params | Should -Not -BeNullOrEmpty + $params.ParameterType.Name | Should -Be 'Hashtable' + } + + } + + Context 'Help Content' { + + It 'has a synopsis' { + $help = Get-Help Invoke-FIDO2Authentication + $help.Synopsis | Should -Not -BeNullOrEmpty + } + + It 'has a description' { + $help = Get-Help Invoke-FIDO2Authentication + $help.Description | Should -Not -BeNullOrEmpty + } + + It 'has examples' { + $help = Get-Help Invoke-FIDO2Authentication + $help.Examples | Should -Not -BeNullOrEmpty + } + + } + + } + +} diff --git a/Tests/New-PASSession.Tests.ps1 b/Tests/New-PASSession.Tests.ps1 index 96a49186..d9b99a62 100644 --- a/Tests/New-PASSession.Tests.ps1 +++ b/Tests/New-PASSession.Tests.ps1 @@ -1237,6 +1237,69 @@ Describe $($PSCommandPath -Replace '.Tests.ps1') { } + Context 'Gen2 with FIDO2' { + + BeforeEach { + + Mock Assert-VersionRequirement -MockWith {} + + Mock Invoke-FIDO2Authentication -MockWith { + [PSCustomObject]@{ + 'CyberArkLogonResult' = 'AAAAAAA\\\REEEAAAAALLLLYYYYY\\\\LOOOOONNNNGGGGG\\\ACCCCCEEEEEEEESSSSSSS\\\\\\TTTTTOOOOOKKKKKEEEEEN' + } + } + + Mock Get-PASServer -MockWith { + [PSCustomObject]@{ + ExternalVersion = '14.6' + } + } + + Mock Get-PASLoggedOnUser -MockWith { + @{'UserName' = 'TestUser' } + } + + $psPASSession.ExternalVersion = '14.6' + $psPASSession.WebSession = New-Object Microsoft.PowerShell.Commands.WebRequestSession + + } + + It 'sends request with UserName parameter' { + New-PASSession -BaseURI 'https://pvwa.cyberark.com' -type FIDO2 -UserName 'TestUser' + Assert-MockCalled Invoke-FIDO2Authentication -Times 1 -Exactly -Scope It + + } + + It 'sends request with expected parameters' { + New-PASSession -BaseURI 'https://pvwa.cyberark.com' -type FIDO2 -UserName 'TestUser' + Assert-MockCalled Invoke-FIDO2Authentication -ParameterFilter { + + $BaseURI -eq 'https://pvwa.cyberark.com/PasswordVault' -and $UserName -eq 'TestUser' + + } -Times 1 -Exactly -Scope It + + } + + It 'throws error when UserName is not provided' { + { New-PASSession -BaseURI 'https://pvwa.cyberark.com' -type FIDO2 } | Should -Throw 'Username is required for FIDO2 authentication. Use -UserName parameter.' + } + + It 'sets expected BaseURI' { + + New-PASSession -BaseURI 'https://pvwa.cyberark.com' -type FIDO2 -UserName 'TestUser' + $Script:psPASSession.BaseURI | Should -Be 'https://pvwa.cyberark.com/PasswordVault' + + } + + It 'sets expected authorization header' { + + New-PASSession -BaseURI 'https://pvwa.cyberark.com' -type FIDO2 -UserName 'TestUser' + $psPASSession.WebSession.Headers['Authorization'] | Should -Be 'AAAAAAA\\\REEEAAAAALLLLYYYYY\\\\LOOOOONNNNGGGGG\\\ACCCCCEEEEEEEESSSSSSS\\\\\\TTTTTOOOOOKKKKKEEEEEN' + + } + + } + } } diff --git a/docs/collections/_commands/New-PASSession.md b/docs/collections/_commands/New-PASSession.md index f25a33c2..7f8ec617 100644 --- a/docs/collections/_commands/New-PASSession.md +++ b/docs/collections/_commands/New-PASSession.md @@ -16,16 +16,18 @@ Authenticates a user to CyberArk Vault/API. ### Gen2 (Default) ``` -New-PASSession [-Credential ] -BaseURI [-newPassword ] [-type ] - [-concurrentSession ] [-PVWAAppName ] [-SkipVersionCheck] [-Certificate ] - [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] [-Confirm] [] +New-PASSession [-Credential ] -BaseURI [-UserName ] + [-newPassword ] [-type ] [-concurrentSession ] [-PVWAAppName ] + [-SkipVersionCheck] [-Certificate ] [-CertificateThumbprint ] [-SkipCertificateCheck] + [-WhatIf] [-Confirm] [] ``` ### ISPSS-URL-ServiceUser ``` New-PASSession -Credential -IdentityTenantURL -PrivilegeCloudURL [-ServiceUser] [-PVWAAppName ] [-SkipVersionCheck] [-Certificate ] - [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] [-Confirm] [] + [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] + [-Confirm] [] ``` ### ISPSS-Subdomain-ServiceUser @@ -39,7 +41,8 @@ New-PASSession -Credential -TenantSubdomain [-ServiceUse ``` New-PASSession -Credential -IdentityTenantURL -PrivilegeCloudURL [-IdentityUser] [-PVWAAppName ] [-SkipVersionCheck] [-Certificate ] - [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] [-Confirm] [] + [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] + [-Confirm] [] ``` ### ISPSS-Subdomain-IdentityUser @@ -54,14 +57,16 @@ New-PASSession -Credential -TenantSubdomain [-IdentityUs New-PASSession -Credential -BaseURI [-UseGen1API] -useRadiusAuthentication [-OTP ] [-OTPMode ] [-OTPDelimiter ] [-RadiusChallenge ] [-connectionNumber ] [-PVWAAppName ] [-SkipVersionCheck] [-Certificate ] - [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] [-Confirm] [] + [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] + [-Confirm] [] ``` ### Gen1 ``` New-PASSession -Credential -BaseURI [-UseGen1API] [-newPassword ] [-connectionNumber ] [-PVWAAppName ] [-SkipVersionCheck] [-Certificate ] - [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] [-Confirm] [] + [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] + [-Confirm] [] ``` ### Gen2Radius @@ -82,8 +87,8 @@ New-PASSession -BaseURI [-UseDefaultCredentials] [-concurrentSession [-UseSharedAuthentication] [-PVWAAppName ] [-SkipVersionCheck] - [-Certificate ] [-CertificateThumbprint ] [-SkipCertificateCheck] [-WhatIf] - [-Confirm] [] + [-Certificate ] [-CertificateThumbprint ] [-SkipCertificateCheck] + [-WhatIf] [-Confirm] [] ``` ### Gen2SAML @@ -386,6 +391,13 @@ Requires IdentityCommand module to be installed for authentication flow to compl See: Get-Help IdentityCommand +### EXAMPLE 31 +``` +New-PASSession -BaseURI https://pvwa.company.com -type FIDO2 -UserName administrator +``` + +Authenticates to CyberArk using FIDO2/WebAuthn hardware security key authentication. + ## PARAMETERS ### -Credential @@ -521,13 +533,14 @@ Accept wildcard characters: False ### -type When using the Gen2 API, specify the type of authentication to use. -Valid values are: - CyberArk - +Valid values are: +- CyberArk - LDAP -- Windows -- Minimum version required 10.4 - RADIUS +- Windows (Minimum version required 10.4) +- RADIUS - PKI - PKIPN +- FIDO2 (Minimum version required 14.4) ```yaml Type: String @@ -907,6 +920,23 @@ Accept pipeline input: True (ByPropertyName) Accept wildcard characters: False ``` +### -UserName +The username for FIDO2 authentication. +When using `-type FIDO2`, specify the username with this parameter (required). +The username identifies the user and their registered security keys. + +```yaml +Type: String +Parameter Sets: Gen2 +Aliases: + +Required: False +Position: Named +Default value: None +Accept pipeline input: True (ByPropertyName) +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/psPAS/Functions/Authentication/New-PASSession.ps1 b/psPAS/Functions/Authentication/New-PASSession.ps1 index 483283e9..12681c02 100644 --- a/psPAS/Functions/Authentication/New-PASSession.ps1 +++ b/psPAS/Functions/Authentication/New-PASSession.ps1 @@ -189,6 +189,15 @@ function New-PASSession { [Alias('UseClassicAPI')] [switch]$UseGen1API, + [Parameter( + Mandatory = $false, + ValueFromPipeline = $false, + ValueFromPipelinebyPropertyName = $true, + ParameterSetName = 'Gen2' + )] + [ValidateNotNullOrEmpty()] + [string]$UserName, + [Parameter( Mandatory = $false, ValueFromPipeline = $false, @@ -254,7 +263,7 @@ function New-PASSession { ValueFromPipelinebyPropertyName = $true, ParameterSetName = 'Gen2Radius' )] - [ValidateSet('CyberArk', 'LDAP', 'Windows', 'RADIUS', 'PKI', 'PKIPN')] + [ValidateSet('CyberArk', 'LDAP', 'Windows', 'RADIUS', 'PKI', 'PKIPN', 'FIDO2')] [string]$type = 'CyberArk', [Parameter( @@ -555,7 +564,7 @@ function New-PASSession { #Get request parameters $boundParameters = $PSBoundParameters | Get-PASParameter -ParametersToRemove Credential, SkipVersionCheck, SkipCertificateCheck, - UseDefaultCredentials, CertificateThumbprint, BaseURI, PVWAAppName, OTP, type, OTPMode, OTPDelimiter, RadiusChallenge, Certificate + UseDefaultCredentials, CertificateThumbprint, BaseURI, PVWAAppName, OTP, type, OTPMode, OTPDelimiter, RadiusChallenge, Certificate, UserName #deal with newPassword SecureString if ($PSBoundParameters.ContainsKey('newPassword')) { @@ -565,7 +574,15 @@ function New-PASSession { } - if ($type -ne 'PKIPN') { + if ($type -eq 'FIDO2') { + # FIDO2 authentication requires username but no password + Assert-VersionRequirement -SelfHosted + + if (-not $PSBoundParameters.Keys.Contains('UserName')) { + throw 'Username is required for FIDO2 authentication. Use -UserName parameter.' + } + + } elseif ($type -ne 'PKIPN') { if ($PSBoundParameters.Keys.Contains('Credential')) { #Add user name from credential object @@ -649,6 +666,11 @@ function New-PASSession { $PASSession = New-IDPlatformToken -tenant_url $LogonRequest['Uri'] -Credential $LogonRequest['Credential'] break } + ( { $type -eq 'FIDO2' } ) { + # Perform FIDO2 Authentication + $PASSession = Invoke-FIDO2Authentication -BaseURI $Uri -UserName $UserName -LogonRequest $LogonRequest + break + } default { #Send Logon Request $PASSession = Invoke-PASRestMethod @LogonRequest diff --git a/psPAS/Private/ConvertFrom-Base64UrlString.ps1 b/psPAS/Private/ConvertFrom-Base64UrlString.ps1 new file mode 100644 index 00000000..4d0717ee --- /dev/null +++ b/psPAS/Private/ConvertFrom-Base64UrlString.ps1 @@ -0,0 +1,45 @@ +function ConvertFrom-Base64UrlString { + <# + .SYNOPSIS + Converts a Base64Url-encoded string to bytes + + .DESCRIPTION + Converts a Base64Url-encoded string (URL-safe Base64 without padding) to a byte array. + This format is used in FIDO2/WebAuthn specifications. + + .PARAMETER InputString + The Base64Url-encoded string to convert + + .EXAMPLE + ConvertFrom-Base64UrlString -InputString 'SGVsbG8gV29ybGQ' + + Converts the Base64Url string to bytes + + .NOTES + Base64Url encoding uses - and _ instead of + and / and removes padding (=) + #> + [CmdletBinding()] + [OutputType([byte[]])] + param( + [Parameter( + Mandatory = $true, + ValueFromPipeline = $true, + ValueFromPipelinebyPropertyName = $true + )] + [string]$InputString + ) + + process { + # Convert Base64Url to standard Base64 + $base64 = $InputString.Replace('-', '+').Replace('_', '/') + + # Add padding if necessary + $padding = 4 - ($base64.Length % 4) + if ($padding -ne 4) { + $base64 += '=' * $padding + } + + # Convert to bytes + [System.Convert]::FromBase64String($base64) + } +} diff --git a/psPAS/Private/Invoke-FIDO2Authentication.ps1 b/psPAS/Private/Invoke-FIDO2Authentication.ps1 new file mode 100644 index 00000000..df135533 --- /dev/null +++ b/psPAS/Private/Invoke-FIDO2Authentication.ps1 @@ -0,0 +1,156 @@ +function Invoke-FIDO2Authentication { + <# + .SYNOPSIS + Performs FIDO2 authentication using DSInternals.Win32.WebAuthn + + .DESCRIPTION + Handles the two-step FIDO2 authentication flow: + 1. Request assertion options from CyberArk API + 2. Use FIDO2 device to generate assertion + 3. Submit assertion back to CyberArk API + + .PARAMETER BaseURI + The base URI for the CyberArk PVWA + + .PARAMETER UserName + The username for FIDO2 authentication + + .PARAMETER LogonRequest + Hashtable containing the logon request parameters + + .EXAMPLE + Invoke-FIDO2Authentication -BaseURI 'https://pvwa.example.com/PasswordVault' -UserName 'administrator' -LogonRequest $request + + .NOTES + Requires Windows 10 1903+ and the DSInternals.Win32.WebAuthn assembly + #> + [CmdletBinding()] + [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'LogonRequest', Justification = 'LogonRequest is used within scriptblock via closure')] + param( + [Parameter(Mandatory = $true)] + [string]$BaseURI, + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string]$UserName, + + [Parameter(Mandatory = $false)] + [hashtable]$LogonRequest + ) + + begin { + # FIDO2 requires Windows 10+ + if ((Test-IsCoreCLR) -and -not $IsWindows) { + throw 'FIDO2 authentication is only supported on Windows platforms' + } + + # Load DSInternals.Win32.WebAuthn + try { + $assemblyPath = Join-Path $Script:ModuleRoot 'lib\DSInternals.Win32.WebAuthn.dll' + + if (Test-Path $assemblyPath) { + Add-Type -Path $assemblyPath -ErrorAction Stop + } else { + throw "DSInternals.Win32.WebAuthn assembly not found at: $assemblyPath" + } + } catch { + throw "Failed to load WebAuthn Interop Assembly: $($_.Exception.Message)" + } + } + + process { + try { + # Helper to add LogonRequest parameters to API call parameters + $addLogonParams = { + param($Target) + 'UseDefaultCredentials', 'SkipCertificateCheck', 'Certificate', 'CertificateThumbprint' | ForEach-Object { + if ($LogonRequest.ContainsKey($_)) { $Target[$_] = $LogonRequest[$_] } + } + } + + $challengeParams = @{ + Uri = "$BaseURI/api/auth/fido/logon" + Method = 'POST' + Body = (@{ username = $UserName; type = 'fido' } | ConvertTo-Json) + } + & $addLogonParams $challengeParams + + $challengeResponse = Invoke-PASRestMethod @challengeParams + + $assertionOptions = $challengeResponse.assertionOptions + + # Use FIDO2 device to generate assertion + $webAuthnApi = New-Object DSInternals.Win32.WebAuthn.WebAuthnApi + $challengeBytes = ConvertFrom-Base64UrlString -InputString $assertionOptions.challenge + + # Build allowed credentials list + $allowCredentials = New-Object 'System.Collections.Generic.List[DSInternals.Win32.WebAuthn.PublicKeyCredentialDescriptor]' + if ($assertionOptions.allowCredentials) { + $assertionOptions.allowCredentials | ForEach-Object { + $credId = ConvertFrom-Base64UrlString -InputString $_.id + $allowCredentials.Add((New-Object DSInternals.Win32.WebAuthn.PublicKeyCredentialDescriptor -ArgumentList @( + $credId, + [DSInternals.Win32.WebAuthn.AuthenticatorTransport]::NoRestrictions, + 'public-key' + ))) + } + } + + # Set user verification to required + $userVerification = [DSInternals.Win32.WebAuthn.UserVerificationRequirement]::Required + + # Create CollectedClientData with correct origin (bypasses DSInternals' UriBuilder :80/ issue) + $clientData = New-Object DSInternals.Win32.WebAuthn.FIDO.CollectedClientData + $clientData.Type = 'webauthn.get' + $clientData.Challenge = $challengeBytes + $clientData.Origin = "https://$($assertionOptions.rpId)" + $clientData.CrossOrigin = $false + + $assertion = $webAuthnApi.AuthenticatorGetAssertion( + $assertionOptions.rpId, $clientData, $userVerification, + [DSInternals.Win32.WebAuthn.AuthenticatorAttachment]::Any, 60000, $allowCredentials + ) + + # Build the response payload + $credentialIdBytes = $allowCredentials[0].Id + + $assertionResponse = [ordered]@{ + Id = [Convert]::ToBase64String($credentialIdBytes).Replace('+', '-').Replace('/', '_').TrimEnd('=') + RawId = [Convert]::ToBase64String($credentialIdBytes).Replace('+', '-').Replace('/', '_').TrimEnd('=') + Type = 'public-key' + Extensions = @{} + Response = [ordered]@{ + AuthenticatorData = [Convert]::ToBase64String($assertion.AuthenticatorData).Replace('+', '-').Replace('/', '_').TrimEnd('=') + ClientDataJson = [Convert]::ToBase64String($assertion.ClientDataJson).Replace('+', '-').Replace('/', '_').TrimEnd('=') + Signature = [Convert]::ToBase64String($assertion.Signature).Replace('+', '-').Replace('/', '_').TrimEnd('=') + UserHandle = $null + } + } + + if ($assertion.UserHandle -and $assertion.UserHandle.Length -gt 0) { + $assertionResponse.Response.UserHandle = [Convert]::ToBase64String($assertion.UserHandle).Replace('+', '-').Replace('/', '_').TrimEnd('=') + } + + $additionalInfo = $assertionResponse | ConvertTo-Json -Depth 10 -Compress + $additionalInfoBase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($additionalInfo)).Replace('+', '-').Replace('/', '_').TrimEnd('=') + + # Submit assertion to CyberArk API + $authParams = @{ + Uri = "$BaseURI/api/auth/fido/logon" + Method = 'POST' + Body = (@{ userName = $UserName; AdditionalInfo = $additionalInfoBase64 } | ConvertTo-Json) + SessionVariable = 'FIDOSession' + } + & $addLogonParams $authParams + + $authResponse = Invoke-PASRestMethod @authParams + + return $authResponse + + } catch { + throw "FIDO2 authentication failed: $($_.Exception.Message)" + } + } + + end {} +} diff --git a/psPAS/en-US/psPAS-help.xml b/psPAS/en-US/psPAS-help.xml index 1f7927cd..57563779 100644 --- a/psPAS/en-US/psPAS-help.xml +++ b/psPAS/en-US/psPAS-help.xml @@ -29332,10 +29332,11 @@ PS C:\> New-PASReportSchedule -version 1 -type 'Report' -subType 'CyberArk.Re When using the Gen2 API, specify the type of authentication to use. Valid values are: - CyberArk - LDAP - - Windows - - Minimum version required 10.4 - RADIUS + - Windows (Minimum version required 10.4) + - RADIUS - PKI - PKIPN + - FIDO2 (Minimum version required 14.4) String @@ -29459,6 +29460,18 @@ PS C:\> New-PASReportSchedule -version 1 -type 'Report' -subType 'CyberArk.Re False + + UserName + + The username for FIDO2 authentication. When using `-type FIDO2`, specify the username with this parameter (required). The username identifies the user and their registered security keys. + + String + + String + + + None + New-PASSession @@ -30024,10 +30037,11 @@ PS C:\> New-PASReportSchedule -version 1 -type 'Report' -subType 'CyberArk.Re When using the Gen2 API, specify the type of authentication to use. Valid values are: - CyberArk - LDAP - - Windows - - Minimum version required 10.4 - RADIUS + - Windows (Minimum version required 10.4) + - RADIUS - PKI - PKIPN + - FIDO2 (Minimum version required 14.4) String @@ -31163,10 +31177,11 @@ PS C:\> New-PASReportSchedule -version 1 -type 'Report' -subType 'CyberArk.Re When using the Gen2 API, specify the type of authentication to use. Valid values are: - CyberArk - LDAP - - Windows - - Minimum version required 10.4 - RADIUS + - Windows (Minimum version required 10.4) + - RADIUS - PKI - PKIPN + - FIDO2 (Minimum version required 14.4) String @@ -31452,6 +31467,18 @@ PS C:\> New-PASReportSchedule -version 1 -type 'Report' -subType 'CyberArk.Re False + + UserName + + The username for FIDO2 authentication. When using `-type FIDO2`, specify the username with this parameter (required). The username identifies the user and their registered security keys. + + String + + String + + + None + @@ -31713,6 +31740,13 @@ New-PASSession -BaseURI $url -type PKIPN -Certificate $Cert See: Get-Help IdentityCommand + + -------------------------- EXAMPLE 31 -------------------------- + New-PASSession -BaseURI https://pvwa.company.com -type FIDO2 -UserName administrator + + Authenticates to CyberArk using FIDO2/WebAuthn hardware security key authentication. + + diff --git a/psPAS/lib/DSInternals.LICENSE b/psPAS/lib/DSInternals.LICENSE new file mode 100644 index 00000000..699d5dfb --- /dev/null +++ b/psPAS/lib/DSInternals.LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2015-2025 Michael Grafnetter + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/psPAS/lib/DSInternals.Win32.WebAuthn.dll b/psPAS/lib/DSInternals.Win32.WebAuthn.dll new file mode 100644 index 0000000000000000000000000000000000000000..7dfde5624f85520440d134bd3d29264a9d7a46ee GIT binary patch literal 102824 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≤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

};^br~`gc%n)AxCOolS25uu>5AOtD#CY+x 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