2

I try to adapt a working curl command to powershell

this is the working curl from postman:

curl -X POST \
  https://test.portal.com/api/v1/login \
  -H 'authorization: Basic ADSFws343==' \
  -H 'cache-control: no-cache' \
  -H 'content-type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW' \
  -H 'postman-token: 06944eab-3634-dd34-4634-0966bd8872c5' \
  -F 'user[email]=xxx@xxx.com' \
  -F 'user[password]=xxx'

This creates the following raw data

POST /api/v1/login HTTP/1.1
cache-control: no-cache
Postman-Token: 80b2b345-6f5d-4c63-bedf-c65038b1e5df
Authorization: Basic ADSFws343==
User-Agent: PostmanRuntime/3.0.11-hotfix.2
Accept: */*
Host: test.portal.com
accept-encoding: gzip, deflate
content-type: multipart/form-data; boundary=--------------------------266295771457397823506834
content-length: 309
Connection: close

----------------------------266295771457397823506834
Content-Disposition: form-data; name="user[email]"

xxx@xxx.com
----------------------------266295771457397823506834
Content-Disposition: form-data; name="user[password]"

xxx
----------------------------266295771457397823506834--

and I get the following response:

HTTP/1.1 201 Created
Server: nginx/1.8.0
Date: Tue, 06 Jun 2017 13:42:54 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: close
Status: 201 Created
Cache-Control: max-age=0, private, must-revalidate
X-XSS-Protection: 1; mode=block
X-Request-Id: d1dd86e7-325a-43d5-bfda-46a9df4cc660
ETag: W/"3b793a3a1971dac89b48633e0e031237"
X-Frame-Options: SAMEORIGIN
X-Runtime: 0.115464
X-Content-Type-Options: nosniff
X-Powered-By: Phusion Passenger 5.1.4

with the expected data in the body.


Now I struggle to adapt the code to powershell

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$boundary = "--------------------------210183530616764504332035" 
$URL = "https://test.portal.com/api/v1/login"
$email = "xxx@xxx.com"
$password= "xxx"

$headers = @{
    "cache-control"="no-cache";
    "Postman-Token"="3cf33ce8-716e-4ac1-b2da-c46df65d5307"
    Authorization="Basic ADSFws343==";
    "content-type"="multipart/form-data; boundary=$boundary"
    "User-Agent"="PostmanRuntime/3.0.11-hotfix.2"
    "Accept"="*/*"
    "accept-encoding"="gzip, deflate"
}

$LF = "`n"
$bodyLines = (
    "--$boundary",
    "Content-Disposition: form-data; name=`"user[email]`"$LF", 
    $email,
    "--$boundary",
    "Content-Disposition: form-data; name=`"user[password]`"$LF",
    $password,
    "--$boundary--$LF"
    ) -join $LF

$response = Invoke-RestMethod -Uri $URL -Headers $headers -Method Post -TimeoutSec 20 -Body $bodyLines -DisableKeepAlive

This throws an exception:

Invoke-RestMethod : Incomplete response received from application

and creates the following raw data

POST https://test.portal.com/api/v1/login HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------210183530616764504332065
Accept: */*
Postman-Token: 3cf33ce8-716e-4ac1-b2da-c46df65d5307
Authorization: Basic ADSFws343==
accept-encoding: gzip, deflate
cache-control: no-cache
User-Agent: PostmanRuntime/3.0.11-hotfix.2
Host: test.portal.com
Content-Length: 300
Connection: Close

----------------------------210183530616764504332065
Content-Disposition: form-data; name="user[email]"

xxx@xxx.com
----------------------------210183530616764504332065
Content-Disposition: form-data; name="user[password]"

xxx
----------------------------210183530616764504332065--

and this response

HTTP/1.1 502 Bad Gateway
Server: nginx/1.8.0
Date: Tue, 06 Jun 2017 14:02:23 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 54
Connection: close
Status: 502 Bad Gateway
cache-control: no-cache, no-store, must-revalidate

<h2>Incomplete response received from application</h2>

Where is my fault or what is missing? The only difference in the Raw Data is the somehow shorter content-length. even the boundarys have the same length as the postman curl

Thank you for taking your time

Edit: Changed the Headers to hash-table without effect and changed the powershell code to simulate the postman curl more accurate

Final Solution: Problem was the wrong format of the body. Changing

$LF = "`n"

to

$LF = "`r`n"

fixed the problem!

burglar
  • 21
  • 1
  • 5
  • Do you control the server? Have you done any analysis on the differences yet? You might try changing the user agent. Also if you use `Invoke-WebRequest` instead of `Invoke-RestMethod` you'll get more data about what is returned by the server (codes, messages, headers). – briantist Jun 06 '17 at 15:01
  • "Get-Help Invoke-WebRequest -Full" and Invoke-RestMethod ocourse – Jaqueline Vanek Jun 06 '17 at 15:34
  • for a start, you might wanna try something along the lines of $headers = @{'header1 name' = 'header1 value';'header2 name' = 'header2 value'} also you may (or may not) wanna use "here strings" for the "body" part – Jaqueline Vanek Jun 06 '17 at 15:53
  • briantist: I have no control over the server and can't analyse beyond my client sadly. @JaquelineVanek fixed the headers but it didn't changes the response. The body part is formated like this because of the lack of multipart/form-data support by the cmdlets. I used the workaround from [link](https://stackoverflow.com/questions/25075010/upload-multiple-files-from-powershell-script) which seems to fit the outcome if I compare the raw data send in fiddler – burglar Jun 07 '17 at 05:24

3 Answers3

0

Use hashtables for your headers. Sometimes you need to wrap them in a array

$headers = @{
    authorization="Basic ADSFws343==";
    "cache-control"="no-cache";
    "content-type"="multipart/form-data";
    boundary="----WebKitFormBoundary7MA4YWxkTrZu0gW";
    "postman-token"="06944eab-3634-dd34-4634-0966bd8872c5"
}

$post =@{
    email="xxx@xxx.com"
    password="xxxxxxxx"
}

Invoke-WebRequest -Uri "https://test.portal.com/api/v1/login" -Method POST -Headers $headers -Body $Post -SessionVariable WebSession
$websession
ArcSet
  • 6,518
  • 1
  • 20
  • 34
  • that "boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW" bothers me ;) – Jaqueline Vanek Jun 06 '17 at 15:54
  • fixed the headers as suggested, response remains the same. Edited the original post to reflect the changes and make it easier for the reader to compare. The $post data can't be simplified like that because of the lack of multipart/form-data support of the cmdlets. – burglar Jun 07 '17 at 05:31
0

try something like

$user = 'user'
$password= 'passwd'

$params = @{
    Uri = 'https://httpbin.org/basic-auth/user/passwd'
    Method = 'Get'
    Headers = @{
        Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($user):$($password)"))
    }
}

Invoke-RestMethod @params

what bothers me is that you seem to be using both HTTP BasicAuth and also user/pass in the body, submitting a form. Perhaps something like

$user = 'user'
$password= 'passwd'

$boundary = '----------------------------266295771457397823506834'

$body = @"
$boundary
Content-Disposition: form-data; name="user[email]"

$user
$boundary
Content-Disposition: form-data; name="user[password]"

$password
$boundary--
"@

$params = @{
    Uri = 'https://httpbin.org/basic-auth/user/passwd'
    Method = 'Post'
    Headers = @{
        Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($user):$($password)"))
        'Content-Type' = "multipart/form-data; boundary=$boundary"
    }
    Body = $body
}

Invoke-RestMethod @params
Jaqueline Vanek
  • 1,100
  • 10
  • 20
  • The authorization in the header is working fine as it is it seems. If I leave it out or mess with it I get an unauthorized response. But its just for the connection. The fields in the body are the credentials for access to the API behind it, I'm forced to send it as multipart/form-data. In response I get a token for further communication with the API. Thank you – burglar Jun 07 '17 at 16:33
  • not sure but you may also have wrong number of "----"'s in the "boundary" or something else silly going on. Id generally sniff the traffic and try to replicate it exactly in powershell. redirects, cookies/session variable etc – Jaqueline Vanek Jun 07 '17 at 16:46
  • thank you for your efforts, guess without you pointing me to the boundaries I wouldn't have found the problem! Although the boundaries weren't the problem, it was very close: the correct linebreak due to the rfc for multipart/formdata isn't just LF but CR LF. So after changing the $LF = "`n" to $LF = "`r`n" it works like a charm! – burglar Jun 08 '17 at 13:08
0

The body of the multipart/formdata has the wrong format, needs to be CR LF instead of just LF.

Change

$LF = "`n"

to

$LF = "`r`n"

to make it work

burglar
  • 21
  • 1
  • 5