Hi all, I’m trying to craft a media upload and post script in powershell.
Using postman, the upload works. Using the code snippet for powershell postman produces doesn’t work.
I have tried other methods, but always receive
{"errors":[{"code":32,"message":"Could not authenticate you."}]}
My code is below:

#######################################

# generate variables for image upload #

#######################################

Add-Type -AssemblyName System.Security

$nonce = [System.Convert]::ToBase64String(([System.Text.Encoding]::ASCII.GetBytes("$([System.DateTime]::Now.Ticks.ToString())12345"))).Replace('=', 'g') 

$EpochTimeNow = [System.DateTime]::UtcNow - [System.DateTime]::ParseExact("01/01/1970", "dd/MM/yyyy", $null) 

$timestamp = [System.Convert]::ToInt64($EpochTimeNow.TotalSeconds).ToString(); 

$SignatureBase = "$([System.Uri]::EscapeDataString($HttpEndPoint))&" 

$SignatureParams = @{ 

    'oauth_consumer_key' = $ApiKey; 

    'oauth_token' = $AccessToken;

    'oauth_signature_method' = 'HMAC-SHA1'; 

    'oauth_timestamp' = $Timestamp;

    'oauth_nonce' = $Nonce; 

    'oauth_version' = '1.0'; 

}

$Message = "test message"

$Username = "digitalfixjimmy"

$SignatureParams.GetEnumerator() | Sort-Object name | ForEach-Object {  

    Write-Host "Adding '$([System.Uri]::EscapeDataString(`"$($_.Key)=$($_.Value)&`"))' to signature string" 

    $SignatureBase += [System.Uri]::EscapeDataString("$($_.Key)=$($_.Value)&".Replace(',','%2C').Replace('!','%21')) 

}             

$SignatureBase = $SignatureBase.TrimEnd('%26') 

$SignatureBase = 'POST&' + $SignatureBase 

Write-Host "Base signature generated '$SignatureBase'" 

## Create the hashed string from the base signature 

$SignatureKey = [System.Uri]::EscapeDataString($ApiSecret) + "&" + [System.Uri]::EscapeDataString($AccessTokenSecret);              

$hmacsha1 = new-object System.Security.Cryptography.HMACSHA1; 

$hmacsha1.Key = [System.Text.Encoding]::ASCII.GetBytes($SignatureKey); 

$OauthSignature = [System.Convert]::ToBase64String($hmacsha1.ComputeHash([System.Text.Encoding]::ASCII.GetBytes($SignatureBase))); 

Write-Host "Using signature '$OauthSignature'"              

## Build the authorization headers using most of the signature headers elements.  This is joining all of the 'Key=Value' elements again 

## and only URL encoding the Values this time while including non-URL encoded double quotes around each value 

$AuthorizationParams = $SignatureParams 

$AuthorizationParams.Add('oauth_signature', $OauthSignature) 

$AuthorizationString = 'OAuth ' 

$AuthorizationParams.GetEnumerator() | Sort-Object name | ForEach-Object { $AuthorizationString += $_.Key + '="' + [System.Uri]::EscapeDataString($_.Value) + '", ' } 

$AuthorizationString = $AuthorizationString.TrimEnd(', ') 

Write-Host "Using authorization string '$AuthorizationString'" 

$Body = "status=$Message" 

Write-Host "Using POST body '$Body'"

$HttpEndPoint = "https://upload.twitter.com/1.1/media/upload.json?media_category=tweet_image"

$multipartContent = [System.Net.Http.MultipartFormDataContent]::new()

$multipartFile = $file

$FileStream = [System.IO.FileStream]::new($multipartFile, [System.IO.FileMode]::Open)

$fileHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")

$fileHeader.Name = "media"

$fileHeader.FileName = $file

$fileContent = [System.Net.Http.StreamContent]::new($FileStream)

$fileContent.Headers.ContentDisposition = $fileHeader

$multipartContent.Add($fileContent)

$stringHeader = [System.Net.Http.Headers.ContentDispositionHeaderValue]::new("form-data")

$stringHeader.Name = "media_category"

$stringContent = [System.Net.Http.StringContent]::new("tweet_image")

$stringContent.Headers.ContentDisposition = $stringHeader

$multipartContent.Add($stringContent)

$body = $multipartContent

$response = Invoke-RestMethod -URI $HttpEndPoint -Method post -Headers @{ 'Authorization' = $AuthorizationString } -Body $body -ContentType "multipart/form-data"

$FileStream.Close()

$response | ConvertTo-Json

‘oauth_consumer_key’ = $ApiKey;
‘oauth_token’ = $AccessToken;
‘oauth_signature_method’ = ‘HMAC-SHA1’;
‘oauth_timestamp’ = $Timestamp;
‘oauth_nonce’ = $Nonce;
‘oauth_version’ = ‘1.0’;

I think these have to be sorted alphabetically according to OAuth spec. Creating a signature | Docs | Twitter Developer Platform

There might be other stuff too Authorizing a request | Docs | Twitter Developer Platform but i don’t know powershell well enough to spot it

1 Like

Thanks, I’ll reorder them and give that a go

Jimmy White

Ok that didnt work :confused:
In forming my string, the uri is
https://upload.twitter.com/1.1/media/upload.json
My auth headers (key values changed for privacy) looks like this:

Name  : Authorization
Value : OAuth include_entities="true", oauth_consumer_key="xxxxxxx", oauth_nonce="xxxxxxxxxxxxx", 
        oauth_signature="xxxxxxxxxxxxxxx%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1618560747", 
        oauth_token="xxxxxxxxxxxxx", oauth_version="1.0"

and my body looks like this

Headers                                                                  
-------                                                                  
{[Content-Disposition, System.String[]]}                                 
{[Content-Type, System.String[]], [Content-Disposition, System.String[]]}