Private/Request-SnowOAuthToken.ps1
|
function Get-SnowOAuthErrorCode { [CmdletBinding()] param([Parameter(Mandatory)][System.Management.Automation.ErrorRecord]$ErrorRecord) $errorBody = if ($ErrorRecord.ErrorDetails) { $ErrorRecord.ErrorDetails.Message } else { $null } $response = if ($ErrorRecord.Exception.PSObject.Properties['Response']) { $ErrorRecord.Exception.Response } else { $null } if (-not $errorBody -and $response -and $response.PSObject.Properties['Content']) { try { $errorBody = $response.Content.ReadAsStringAsync().GetAwaiter().GetResult() } catch { $errorBody = $null } } if (-not $errorBody) { return $null } try { $payload = ConvertFrom-Json -InputObject $errorBody -ErrorAction Stop if ($payload.error -is [string]) { return $payload.error } } catch { return $null } $null } function Request-SnowOAuthToken { <# .SYNOPSIS Obtains an OAuth access token from /oauth_token.do and returns it as a SecureString plus expiry. Supports authorization_code with PKCE, refresh_token, client_credentials, and the legacy password grant. #> [CmdletBinding()] param([Parameter(Mandatory)][pscustomobject]$Session) $clientCredential = if ($Session.PSObject.Properties['ClientCredential']) { $Session.ClientCredential } else { $null } $userCredential = if ($Session.PSObject.Properties['Credential']) { $Session.Credential } else { $null } $clientId = if ($Session.PSObject.Properties['ClientId'] -and $Session.ClientId) { $Session.ClientId } elseif ($clientCredential) { $clientCredential.UserName } else { $null } $sessionRefreshToken = if ($Session.PSObject.Properties['RefreshToken']) { $Session.RefreshToken } else { $null } $form = @{ client_id = $clientId } $refreshing = $false if ($sessionRefreshToken) { $refreshing = $true $form.grant_type = 'refresh_token' $form.refresh_token = [System.Net.NetworkCredential]::new('', $sessionRefreshToken).Password if ($clientCredential) { $form.client_secret = $clientCredential.GetNetworkCredential().Password } } elseif ($Session.PSObject.Properties['GrantType'] -and $Session.GrantType -eq 'authorization_code') { $authorization = Invoke-SnowOAuthAuthorization -BaseUri $Session.BaseUri -ClientId $Session.ClientId ` -RedirectPort $Session.RedirectPort -NoBrowser:$Session.NoBrowser $form.grant_type = 'authorization_code' $form.code = $authorization.Code $form.redirect_uri = $authorization.RedirectUri.AbsoluteUri $form.code_verifier = $authorization.Verifier } elseif ($userCredential) { if ($clientCredential) { $form.client_secret = $clientCredential.GetNetworkCredential().Password } $form.grant_type = 'password' $form.username = $userCredential.UserName $form.password = $userCredential.GetNetworkCredential().Password } else { if (-not $clientCredential) { throw [System.InvalidOperationException]::new('No usable OAuth credentials are available. Run Connect-Snowplow again.') } $form.client_secret = $clientCredential.GetNetworkCredential().Password $form.grant_type = 'client_credentials' } try { $resp = Invoke-RestMethod -Uri ([uri]::new($Session.BaseUri, '/oauth_token.do')) -Method Post ` -Body $form -ContentType 'application/x-www-form-urlencoded' -Headers @{ Accept = 'application/json' } ` -ErrorAction Stop } catch { if ($refreshing -and (Get-SnowOAuthErrorCode -ErrorRecord $_) -eq 'invalid_grant') { $Session.RefreshToken = $null Clear-SnowOAuthRefreshToken -BaseUri $Session.BaseUri -ClientId $clientId throw [System.InvalidOperationException]::new('The saved OAuth refresh token was rejected. Run Connect-Snowplow again to sign in.') } $status = if ($_.Exception.PSObject.Properties['Response'] -and $_.Exception.Response) { [int]$_.Exception.Response.StatusCode } else { $null } $statusText = if ($status) { " (HTTP $status)" } else { '' } if ($refreshing) { throw [System.InvalidOperationException]::new("OAuth refresh request failed$statusText. The saved refresh token was retained; retry the request.") } throw [System.InvalidOperationException]::new("OAuth token request failed$statusText. Check the OAuth application, redirect URI, and scopes.") } finally { $form = $null } $accessToken = $resp.PSObject.Properties['access_token'] if (-not $accessToken -or -not $accessToken.Value) { throw [System.InvalidOperationException]::new('OAuth token response was invalid. Run Connect-Snowplow again to sign in.') } $expiresIn = $resp.PSObject.Properties['expires_in'] $lifetime = if ($expiresIn -and $expiresIn.Value) { [int]$expiresIn.Value } else { 1800 } $refreshToken = $resp.PSObject.Properties['refresh_token'] if ($refreshToken -and $refreshToken.Value) { $Session.RefreshToken = ConvertTo-SecureString -String ([string]$refreshToken.Value) -AsPlainText -Force } $clientId = if ($Session.PSObject.Properties['ClientId']) { $Session.ClientId } else { $null } $sessionRefreshToken = if ($Session.PSObject.Properties['RefreshToken']) { $Session.RefreshToken } else { $null } if ($sessionRefreshToken -and $clientId) { Write-SnowOAuthRefreshToken -BaseUri $Session.BaseUri -ClientId $clientId -RefreshToken $sessionRefreshToken } [pscustomobject]@{ AccessToken = ConvertTo-SecureString -String ([string]$accessToken.Value) -AsPlainText -Force ExpiresAt = [datetime]::UtcNow.AddSeconds($lifetime) } } |