Private/Invoke-DuneApiAuthSocial.ps1
|
function Invoke-DuneApiAuthSocial { [CmdletBinding()] param( [Parameter(Mandatory)] [ValidateSet("Prod", "Dev","Test","Local")] [string]$DuneInstance, [Parameter(Mandatory)] [string]$Tenant ) begin { $DuneApiUrl = Get-DuneApiUrl -DuneInstance $DuneInstance $DuneAuthUrl = Get-DuneAuthUrl -DuneInstance $DuneInstance } process { # Function to get an available port function Get-AvailablePort { param( [int]$StartPort = 49152, [int]$EndPort = 65535, [int]$MaxAttempts = 50 ) $usedPorts = @() try { # Get all ports currently in use $netstatOutput = netstat -ano $usedPorts = $netstatOutput | Select-String "LISTENING" | % { $parts = $_ -split '\s+' $portPart = $parts | ? { $_ -match ':\d+$' } | Select-Object -First 1 if ($portPart) { [int]($portPart -split ':')[-1] } } | ? { $_ -ge $StartPort -and $_ -le $EndPort } | Sort-Object -Unique } catch { Write-Warning "Could not retrieve used ports via netstat: $_" } # Try to find an available port $attempts = 0 do { $port = Get-Random -Minimum $StartPort -Maximum $EndPort $attempts++ if ($port -notin $usedPorts) { # Double-check by trying to create a listener on this port try { $testListener = New-Object System.Net.HttpListener $testListener.Prefixes.Add("http://localhost:$port/") $testListener.Start() $testListener.Stop() $testListener.Close() return $port } catch { Write-Debug "Port $port is in use, trying another..." } } } while ($attempts -lt $MaxAttempts) throw "Could not find an available port after $MaxAttempts attempts" } # Get an available port with retry logic $Port = Get-AvailablePort $CallbackUrl = "http://localhost:$Port/" Write-Debug "Found available port: $Port" # starting a local http listener $Listener = $null try { $Listener = New-Object System.Net.HttpListener $Listener.Prefixes.Add($CallbackUrl) $Listener.Start() Write-Debug "Started HTTP Listener: $CallbackUrl" } catch { throw "Could not start HTTP Listener: $_" } Write-Host "Please login to browser..." try { # open browser with dynamic callback URL Start-Process "${DuneAuthUrl}?tenantname=${Tenant}&continue=${CallbackUrl}" # waiting for Redirect-Request $Context = $Listener.GetContext() # blocks until it gets called $Request = $Context.Request Write-Debug "Received request from: $($Request.Url)" $Session = if ($Request.Url.Query -match 's=(?<s>[^&]+)') { $Matches.s } # not sure if needed $TempToken = if ($Request.Url.Query -match 'tmptok=(?<tmptok>[^&]+)') { $Matches.tmptok } $ErrorCode = if ($Request.Url.Query -match 'f=(?<f>[^&]+)') { $Matches.f } $ErrorMessage = if ($Request.Url.Query -match 'm=(?<m>[^&]+)') { $Matches.m } Write-Debug "TempToken: $TempToken" # response to browser - send response BEFORE closing $ResponseString = '<html><head><style>body { font-family: "Inter", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }</style></head><body style="color:#e3e3e3; background-color:black;"><p>Login successfull. Please close this window</p></body></html>' $Buffer = [System.Text.Encoding]::UTF8.GetBytes($ResponseString) try { $Context.Response.ContentLength64 = $Buffer.Length $Context.Response.OutputStream.Write($Buffer, 0, $Buffer.Length) $Context.Response.OutputStream.Flush() } catch { Write-Warning "Error writing response: $_" } finally { $Context.Response.Close() } if ($ErrorCode) { if ($TempToken) { Write-Error -ErrorAction Continue "Received token with error $($ErrorCode): $ErrorMessage" } else { throw "Received error $($ErrorCode): $ErrorMessage" } } $ReturnedTokens = Get-DuneJWTTokens -DuneInstance $DuneInstance -Tenant $Tenant -TemporaryToken $TempToken $Token = $ReturnedTokens.accesstoken $RefreshToken = $ReturnedTokens.refreshToken if ($Token) { $ParsedToken = Parse-JwtToken -Token $Token $TokenExpiryDate = [System.DateTimeOffset]::FromUnixTimeSeconds($ParsedToken.exp).DateTime # save auth info $Script:DuneSession = [PSCustomObject]@{ Type = 'SocialLogin' DuneApiUrl = $DuneApiUrl Token = ($Token | ConvertTo-SecureString -AsPlainText -Force) ExpiryDate = $TokenExpiryDate RefreshToken = ($RefreshToken | ConvertTo-SecureString -AsPlainText -Force) Tenant = $Tenant } Write-Verbose "Login successfull" Save-DuneSession } else { throw "Token not set" } } catch { throw "Could not process HTTP Listener: $_" } finally { # closing the HTTP Listener if ($Listener) { try { $Listener.Stop() $Listener.Close() } catch { Write-Debug "Error closing listener: $_" } } } } end {} } |