Authenticating Web Service Requests with PowerShell

Authenticating Web Service Requests with PowerShell

Many Web services don't require a logon, but here's how to handle the ones that do

I was recently doing a presentation about using PowerShell to access Web services, covering some of the things I've discussed in this column over the past few months, when someone asked me, "What kind of encryption and authentication do Web services use?" I have to admit that I didn't have an answer for a moment. The question had awakened the "Windows security mechanics" part of my brain, which came up all blanks. But then some other neurons woke up, and I remembered the answer. So I replied with something like, "The same as you use all the time on the Web—SSL, user IDs, and passwords."

Setting up an SSL channel in PowerShell with invoke-webrequest or invoke-restmethod is as simple as prefixing the URI with https instead of http, and of course the public/private key pair in the SSL certificate is far more secure than any password. When using a level of security that can use keys up to 4,096 bits in length becomes as ubiquitous and simple as breathing air, you'll be able to exchange user IDs and passwords right on the encrypted channel (and yes, I know, quantum computing will bring out the end of certificates and western civilization, but I'm guessing that's not going to happen for a while yet.)

Thus, about all you need to do to satisfy most Web services is to either do nothing at all (as is the case for many public services) or simply get a key or a userID/key pair. (Note that the key or—as it's increasingly referred to—the secret is basically just the replacement for a password.) We've already seen one example of that when I demonstrated how to ask Bing to look up the city/county/state for a ZIP code. The service is easy to query, but first it requires you to ask Microsoft for a key so that the company can track how many lookups you do.

In the Bing case, I needed only to insert the key in the URL of the request (which, granted, isn't protected by SSL, but that's how Microsoft wants it), but in some cases, I need to get a bit fancier and build it into a HTTP header, which is protected by an SSL connection. Here's how to do it.

In previous examples, you've seen that the National Oceanic and Atmospheric Administration (NOAA) offers a host of interesting geophysical datasets. Most of them don't require user IDs and keys, but NOAA's National Climate Data Center (NCDC) does. Its service is RESTful and offers an overview of how to access the service and where to request a key, which it calls a token.

NCDC has many datasets, but one of the most interesting has the ID GHCND, which is short for Global Historical Climatology Network – Daily summary. You can request daily summaries for any range of dates within a given ZIP code with a URL like this:

https://www.ncdc.noaa.gov/cdo-web/api/v2/data?datasetid=<DSetID>&locationid=ZIP:<zipcode>&startdate=<dstart>&enddate=&<dend>

For example, to look up the summary for February 10, 2016, in ZIP code 23456, you would use this URL:

https://www.ncdc.noaa.gov/cdo-web/api/v2/data?datasetid=GHCND&locationid=ZIP:23456&startdate=2016-02-10&enddate=2016-02-10

By now, you can probably put together a few PowerShell cmdlets to make NCDC return that summary, but an invoke-webrequest or invoke-restmethod would fail with a 400 error: Token parameter is required. Assume, then, that you applied for and got a token from the NCDC URL cited earlier, and its value was XYZ. (It wouldn't be, as the keys are fairly long.)  To get NCDC to fulfill your request, your request must contain this text in the HTTP header:

token = XYZ

Invoke-webrequest and invoke-restmethod both have a -headers parameter that accepts a hash table of name/value pairs, so you can create one like this:

[email protected]{token="XYZ"}

You can then just pass that like -headers $HeaderPairs. Put that all together, and you'll come up with something like this:

[email protected]{token='XYZ'}
$URI = 'https://www.ncdc.noaa.gov/cdo-web/api/v2/data?datasetid=GHCND&locationid=ZIP:23456&startdate=2016-02-10&enddate=2016-02-10'
$result = irm $URI -headers $HeaderPairs

 

Because you've used invoke-restmethod rather than invoke-webrequest, the results have already been de-JSONed into a PowerShell object with the attribute Results, so type the following to see the results:

$result.results

You'll see eight data blocks that look like this one:

date       : 2016-02-10T00:00:00
datatype   : TMAX
station    : GHCND:USC00440385
attributes : ,,H,
value      : 94

 

The interesting parts are the datatype, which identifies that piece of data (TMAX is the highest temperature measured in the day, and TMIN is the lowest measured) and value, which is -6 for TMIN and 94 for TMAX. Yes, that's a big range, but both numbers are the Celsius temperature in tenths of degrees, so the lowest was -0.6 C and the highest was 9.4 C, about 31 degrees and 49 degrees Fahrenheit.

So, to wrap up, many Web services won't need authentication, but the ones that do will usually need something as simple as a key or perhaps a user ID/key pair. Many services put the key into the URL, but the ones that have their "headers" straight, so to speak, many need it in the header, and now you know how to pass that in PowerShell. (And don't forget to use https in the URL!) See you next month.

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish