Public/Users/Send-IDUserIdentityVerification.ps1
|
# .ExternalHelp IdentityCommand-help.xml function Send-IDUserIdentityVerification { [CmdletBinding(SupportsShouldProcess)] param( [parameter( Mandatory = $true, ValueFromPipelinebyPropertyName = $true )] [ValidateNotNullOrEmpty()] [Alias('Uuid')] [String]$ID ) BEGIN {}#begin PROCESS { if ($PSCmdlet.ShouldProcess($ID, 'Send Identity Verification')) { #Step 1: start the challenge for this user, listing their enrolled mechanisms $Verification = Start-IdentityVerification -ID $ID $Mechanisms = @($Verification.ReturnData.Challenges.Mechanisms) if ($Mechanisms.Count -eq 0) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( "User '$ID' has no enrolled identity verification mechanisms.", 'MechanismNotFound', [System.Management.Automation.ErrorCategory]::ObjectNotFound, $ID ) ) } #Same interactive picker used to select a mechanism during login. Must be passed via #-Mechanisms, not piped - piping an array auto-enumerates it onto the pipeline one #element at a time, causing Select-ChallengeMechanism to run once per mechanism (each #seeing a count of 1, so no prompt) instead of once with the full list. $Mechanism = Select-ChallengeMechanism -Mechanisms $Mechanisms #Same answer collection used during login. No credential exists for the target user in #this admin-initiated flow, so the UP/SMS password-based answer will be empty - harmless, #since those mechanisms aren't relevant to verifying someone else's identity. $Answer = Get-MechanismAnswer -Mechanism $Mechanism -Credential ([PSCredential]::Empty) $Request = @{ 'URI' = "$($ISPSSSession.tenant_url)/CDirectoryService/SendIdentityVerification" 'Method' = 'POST' } $Body = [ordered]@{ 'UUID' = $ID 'TenantId' = $ISPSSSession.TenantId 'SessionId' = $Verification.ReturnData.SessionId 'MechanismId' = $Mechanism.MechanismId } #Mirrors Start-AdvanceAuthentication's Action resolution for the login flow switch ($Mechanism) { { $($PSItem.AnswerType) -like 'Start*Oob' } { #StartOOB begins the waiting period for MFA approval $Body['Action'] = 'StartOOB' #Sent as raw UTF8 bytes rather than a String so ParameterBinding/module logging #of this call records a non-revealing type name instead of the literal content $Request['Body'] = [System.Text.Encoding]::UTF8.GetBytes($($Body | ConvertTo-Json)) $null = Invoke-IDRestMethod @Request } { $($PSItem.Name) -match 'EMAIL|OTP|U2F|QR|PF' } { #Poll for response $Body['Action'] = 'Poll' break } { $($PSItem.Name) -match 'SQ|UP|OATH|SMS|RESET' } { #Provide Answer Directly $Body['Action'] = 'Answer' $Body['Answer'] = Unprotect-Answer $Answer break } } #Sent as raw UTF8 bytes rather than a String so ParameterBinding/module logging of this #call records a non-revealing type name instead of the literal request content $Request['Body'] = [System.Text.Encoding]::UTF8.GetBytes($($Body | ConvertTo-Json)) $Result = Invoke-IDRestMethod @Request while ($Result.Summary -match 'OobPending') { #Polls every second while the challenge is pending Start-Sleep 1 $Result = Invoke-IDRestMethod @Request } $Result } }#process END {}#end } |