public/Get-XAccessToken.ps1
function Get-XAccessToken { [CmdletBinding()] param ( [Parameter(Mandatory=$true)][string]$ClientId, [Parameter(Mandatory=$true)][string]$ClientSecret, [string]$RedirectUri = "http://127.0.0.1", [string[]]$Scopes = @("tweet.read", "tweet.write", "users.read", "offline.access") ) # Generate PKCE code verifier and challenge $codeVerifier = -join ((65..90) + (97..122) | Get-Random -Count 128 | % {[char]$_}) $sha256 = [System.Security.Cryptography.SHA256]::Create() $codeChallenge = [Convert]::ToBase64String($sha256.ComputeHash([Text.Encoding]::UTF8.GetBytes($codeVerifier))) -replace '=','' -replace '/','_' -replace '\+','-' # Build authorization URL $authUrl = "https://twitter.com/i/oauth2/authorize?response_type=code&client_id=$ClientId&redirect_uri=$([Uri]::EscapeDataString($RedirectUri))&scope=$([Uri]::EscapeDataString($Scopes -join ' '))&state=state&code_challenge=$codeChallenge&code_challenge_method=S256" Write-Output "Opening authorization URL: $authUrl" # Open browser Start-Process $authUrl # Start HTTP listener $listener = New-Object System.Net.HttpListener $listener.Prefixes.Add($RedirectUri + "/") try { $listener.Start() Write-Output "Waiting for authorization on $RedirectUri..." } catch { Write-error "Failed to start listener: $_" return } try { $context = $listener.GetContext() $response = $context.Response $code = $context.Request.QueryString["code"] $state = $context.Request.QueryString["state"] $xerror = $context.Request.QueryString["xerror"] Write-Output "Received redirect: Code=$code, State=$state, xerror=$xerror" $html = "<html><body>Authorization complete. You can close this tab. xerror: $xerror</body></html>" $buffer = [Text.Encoding]::UTF8.GetBytes($html) $response.ContentLength64 = $buffer.Length $response.OutputStream.Write($buffer, 0, $buffer.Length) $response.Close() } catch { Write-error "xerror processing redirect: $_" } finally { $listener.Stop() } if ($xerror -or -not $code) { Write-error "Authorization failed. Code: $code, xerror: $xerror. Check browser URL for details." return } # Exchange code for access token $tokenBody = @{ code = $code grant_type = "authorization_code" client_id = $ClientId redirect_uri = $RedirectUri code_verifier = $codeVerifier } | ConvertTo-Json $headers = @{ "Content-Type" = "application/json" } $tokenResponse = Invoke-RestMethod -Uri "https://api.twitter.com/2/oauth2/token" -Method Post -Headers $headers -Body $tokenBody Write-Output "Access Token: $($tokenResponse.access_token)" Write-Output "Refresh Token: $($tokenResponse.refresh_token)" Write-Output "Expires In: $($tokenResponse.expires_in) seconds" $tokenResponse | ConvertTo-Json | Out-File -FilePath "x_tokens.json" } |