This article is based on an answer found and updated on StackOverflow.

I'f you have used PowerShell for much at all, you are probably familiar with Invoke-WebRequest. But did you know you can carry around a session and have it automatically add and update cookies for you?

The Scenario

I had an API I wanted to consume, but it was a private one. That is, It was designed and used for a SPA app to consume. The auth for this API (for the write endpoints anyway) had two requirements: It authorised against cookies and required a csrf token in the requests. Don't get hung up on the why or who wrote it, but my plan was to call it without having to change it.

SessionVariable

It turns out that the command Invoke-WebRequest has an argument -SessionVariable where you can provide an object to store session state. On subsequent requests, you can pass it along again using the argument -WebSession.

So we know how to store the session, how do we post a form? Well, clearly you get the page with the form on it, then post that form back. If you have a proper classic HTML page with a Form on it, you can do a GET to that page and use it by filling it out and executing the provided command.

$myUrl = "http://mydomain.url"  

$response = Invoke-WebRequest -Uri $myUrl -Method GET -SessionVariable mySession

$form = $response.Forms[0]
$form.Fields["username"] = "username"
$form.Fields["password"] = "password"

$response = Invoke-WebRequest -Uri ($myUrl + $form.Action) -WebSession $mySession -Method POST 
$response.StatusDescription #should be OK

You may have to check exactly what format $form.Action takes to see if it is absolute or relative URL, or if it includes the domain or not.

Also, check what your form fields are called. I have a mistake in mine where the field was called user rather than username on one page.

Note that in the first request when you use the -SessionVariable argument, you just provide the name of the variable, and not a variable itself. That is, use mySession rather than $mySession.

If you complete this correctly you should have all the correct cookies passed around. This means that both:

  1. The post will work if there is any cookie verification across the get and post and
  2. The resulting $mySession will contain an authenticated cookie for further authenticated requests.

CSRF

Part two is that I had to parse out csrf. I am still not sure why the requests require the csrf token, probably because it's a BFF API.

Luckily, the login form had a token in the original form, so I was able to just provide that in subsequent requests.

$payload = @{_csrf = $form.Fields["_csrf"]; data = "more data"}

and then can just use the payload as normal:

$response = Invoke-WebRequest -Uri ($myUrl + $form.Action) -WebSession $mySession -Method POST -Body $payload

Of course, your mileage may vary. But this worked for me. The other option would have been to parse out the value from the page, from cookies, or actually, just fix the service to not need it I guess?