curl to Invoke-RestMethod conversion

Welcome Forums General PowerShell Q&A curl to Invoke-RestMethod conversion

Viewing 16 reply threads
  • Author
    Posts
    • #52021
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      I'm trying to convert this curl command to Invoke-RestMethod.

      The curl command is...

      curl --insecure -i  -H "Content-type: application/x-www-form-urlencoded" -H "Accept: appliation/json" --data  "grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string" -X POST https://192.168.110.90/api/common/1.0/oauth/token

      So far I have the following but I'm not sure where to put 'Accept: appliation/json'? Any assistance would be greatly appreciated.

      $Params = @{
          ContentType = 'application/x-www-form-urlencoded' 
          Body = 'grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
          Method = 'Post'
          URI = 'https://192.168.110.90/api/common/1.0/oauth/token'
      }
      
      Invoke-RestMethod @Params
    • #52025
      Moderator
      Topics: 2
      Replies: 525
      Points: 24
      Team Member
      Rank: Member

      Please check if one of the examples below work for you.

      Example 1:

      $Params = @{
          ContentType = 'application/x-www-form-urlencoded' 
          Body = 'grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
          Method = 'Post'
          URI = 'https://192.168.110.90/api/common/1.0/oauth/token'
          Headers = @{'accept'='application/json'}
      }
      
      Invoke-RestMethod @Params
      

      Example 2:

      $Params = @{
          Body = 'grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
          Method = 'Post'
          URI = 'https://192.168.110.90/api/common/1.0/oauth/token'
          Headers = @{'accept'='application/json';'content-type'='application/x-www-form-urlencoded'}
      }
      
      Invoke-RestMethod @Params
      
    • #52041
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      Hi Daniel, thanks for the quick reply.

      Both examples gives me "Invoke-RestMethod : The remote server returned an error: (400) Bad Request."

      I suspect the issue is with Body. RESTful is a pain in the ass.

    • #52047
      Moderator
      Topics: 2
      Replies: 525
      Points: 24
      Team Member
      Rank: Member

      Please check if either of the following works instead.

      $Params = @{
          ContentType = 'application/x-www-form-urlencoded' 
          Method = 'Post'
          URI = 'https://192.168.110.90/api/common/1.0/oauth/token?grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
          Headers = @{'accept'='application/json'}
      }
      Invoke-RestMethod @Params
      
      $Params = @{
          Method = 'Post'
          URI = 'https://192.168.110.90/api/common/1.0/oauth/token?grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
          Headers = @{'accept'='application/json';'content-type'='application/x-www-form-urlencoded'}
      }
      Invoke-RestMethod @Params
      
    • #52178
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      Still getting the (400) Bad Request error Daniel. Thank you so much for your suggestions. I'm going to open a case with the vendor.

    • #52192
      Participant
      Topics: 6
      Replies: 658
      Points: 47
      Rank: Member

      Try this. Specifying the headers this way has worked well for me using other RESTful APIs.

      $headers = New-Object "System.Collections.Generic.Dictionary[[String],[String]]"
      $headers.Add('Content-type', 'application/x-www-form-urlencoded'")
      $headers.Add('Accept', 'application/json')
      
      $args = @{
          Uri = "https://192.168.110.90/api/common/1.0/oauth/token"
          Method = "Post"
          Headers = $headers
          Body = 'grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string'
      }
      
      Invoke-RestMethod @args
      
    • #52222
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      Thanks for the suggestion Curtis. Still no luck. Same 400 error.

      The REST API document (PDF) is here

      It says...

      All access to protected resources requires a valid access token. To obtain an access token, the client must send
      a POST request with the access code. (See the oauth section under Resources for the API.)
      The Steelhead appliance will issue an access token that is valid for the next one hour time period and return that
      token in the body of the POST. If the client script runs for over an hour, the appliance must generate another
      access token when the old one expires. An expired token results in an error with HTTP code 401 and error_id
      AUTH_EXPIRED_TOKEN.
      Finally, the Authorization HTTP Header must be used to pass the token for API authentication. The following
      format should be used:
      Authorization: Bearer 'Access Token encoded in base64 format'
      Example: Authorization: Bearer eyJhdWQiOiAiaHR0cHM6Ly9nZW4tdnNoNDMubGFiLm5idHRlY2guY29tAifQ==

      Below the full doco with the example I was providing before. Maybe you guys will see something I'm missing.

      Issue

      How do you make REST API calls on the Steelhead ?

      Solution

      Starting with RioS 8.5 Steelheads support REST API Access. To enable this feature
      •In the Steelhead web GUI ,
      Configure > Security > REST API Access –> Enable REST API Access
      •Preconfigure the access code
      Configure > Security > REST API Access to display the REST API Access page. –> Click Add Access Code –> Generate New Access Code/Import Existing Access Code –> Add
      •Copy the access code copied from the Management Console REST API Access page into the configuration file of your external script. The script uses the access code to make a call to the Steelhead appliance to request an access token.

      The appliance/system validates the access code and returns an access token for use by the script. Generally the access token is kept by the script for a session only, but note that the script can make many requests using the same access token. These access tokens have some lifetime—usually around an hour —in which they are valid. When they expire, the access code must fetch a new access token. The script uses the access token to make REST API calls with the appliance/system.

      Using CURL to obtain a token

      [user@tbsl6 ~]$ curl –insecure -i -H "Content-type: application/x-www-form-urlencoded" -H "Accept: appliation/json" –data "grant_type=access_code&assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.&state=state_string" -X POST https://192.168.110.90/api/common/1.0/oauth/token

      HTTP/1.1 200 OK
      Date: Fri, 27 Jun 2014 21:33:37 GMT

      Server: PasteWSGIServer/0.5 Python/2.7.3
      Content-type: application/json
      Vary: Accept-Encoding,User-Agent
      Transfer-Encoding: chunked
      {

      "access_token": "eyJhdWQiOiAiaHR0cHM6Ly90......QwMzkwODQxNyIsICJpYXQiOiAiMTQwMzkwNDgxNyJ9",
      "allowed_signature_types": [
      "none"
      ],
      "expires_in": 3600,
      "state": "state_string",
      "token_type": "bearer"
      }

      Note:

      The assertion string, which is of a format 'a.b.c', where a = base64('{"alg":"none"}'), b = base64(access code) and c = empty string. base64() is the function that encodes the string passed to base64 format. Use the corresponding function in your language of implementation. Also, in 'a', the algorithm is 'none', because the only signature method currently supported is 'none'.

      In the example above ,

      assertion=eyJhbGciOiJub25lIn0K.eyJhdWQiOiAiaHR0cHM6Ly90XXXXXXCJpYXQiOiAiMTQwMjY4MjQ5NSJ9.

      a= 'eyJhbGciOiJub25lIn0K' ==> b64encode('{"alg":"none"}\n')
      b= access code from Steelhead
      c= empty string

      Making a REST API authenticated call using CURL

      [user@tbsl6 ~]$ curl –insecure -i -H "Content-type: application/x-www-form-urlencoded" -H "Accept: application/json" -H "Authorization: Bearer eyJhdWQiOiAiaHR0cHM6Ly90......QwMzkwODQxNyIsICJpYXQiOiAiMTQwMzkwNDgxNyJ9" https://192.168.110.90/api/rfwk/1.0/system/uptime

      HTTP/1.1 200 OK
      Date: Fri, 27 Jun 2014 21:45:07 GMT
      Server: PasteWSGIServer/0.5 Python/2.7.3
      Content-type: application/json
      Vary: Accept-Encoding,User-Agent
      Transfer-Encoding: chunked
      {
      "uptime": "3735163904"
      }

    • #52227
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      and here is a Python example...I'm wondering if it's because I'm not doing base64 encoding.

      import os,sys
      import base64
      import json,time
      import httplib, urllib
      
      def base64_encode(s):
         return base64.urlsafe_b64encode(s)
      
      def GET(sh,token,url):
         auth_token="Bearer " + ''.join(token)
         headers = {"Content-type": "application/x-www-form-urlencoded","Accept": "application/json","Authorization": auth_token}
         conn = httplib.HTTPSConnection(sh)
         conn.request("GET", url, auth_token, headers)
         response_data = conn.getresponse().read()
         print "GET:",url," response:\n", response_data
      
      # Main
      steelhead_ip="X.X.X.X"
      access_code="eyJhdWQiOiAiaHR0cHM6Ly90Yi1zZXJ2ZXJzaC9hcGkvY29tbW9uLzEuMC90b2tlbiIsICJpc3MiOiAiaHR0cHM6Ly90Yi1zZXJ2ZXJzaCIsICJwcm4iOiAiYWRtaW4iLCAianRpIjogIjI2NjA5YzM0LWE1ZDAtNGZmZS05YWViLThjMjZhMzMxMTY1MiIsICJleHAiOiAiMCIsICJpYXQiOiAiMTQwMjY4MjQ5NSJ9"
      header_encoded = base64_encode("{\"alg\":\"none\"}\n")
      assertion_string=''.join([header_encoded,'.',access_code,'.',''])
      
      # Create OAuth request
      header = {"Content-type": "application/x-www-form-urlencoded","Accept": "application/json"}
      body = 'grant_type=%s&assertion=%s&state=%s' % ('access_code', assertion_string, 'state_string')
      
      conn = httplib.HTTPSConnection(steelhead_ip)
      conn.request("POST", "/api/common/1.0/oauth/token", body, header)
      response = conn.getresponse()
      rdata = response.read()
      conn.close()
      
      jdata=json.loads(rdata)
      if response.status == 200:
          access_token=jdata['access_token']
          print "\naccess_token=%s\nExpires in=%s\n" % (jdata['access_token'], jdata['expires_in'])
      else:
          print response.status, response.reason
      
      print "Rest API calls using token"
      
      GET(steelhead_ip,access_token,"/api/rfwk/1.0/system/uptime")
      GET(steelhead_ip,access_token,"/api/sh/1.0/status/health")
      GET(steelhead_ip,access_token,"/api/common/1.0/info")
    • #52301
      Moderator
      Topics: 2
      Replies: 525
      Points: 24
      Team Member
      Rank: Member

      Thanks for the link to the PDF and the sample Python code.

      According to the document the API expects a JSON request body too which does not make sense if you look at the Curl and Python examples. However, I've created a couple of examples below of which one will hopefully work for you.

      As I understand (page 3 of the PDF), you'll need to go into appliance portal and generate an access code, and replace the place holders in my examples before going ahead. Obviously the access code provided in the Curl and Python examples won't work for the appliance you've access to.

      I hope that helps. I can offer you to have a Zoom screen sharing session for free if you're still stuck with getting this to work. Unfortunately, I don't have access to this kind of appliance. Please let us know.

      Example 1a – JSON request body and access code with Base64 encoding:

      $uri = 'https://192.168.110.90/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}\n"
      $accessCode = 'PasteAccessCodeFromAppliancePortalHere'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($algorithm))
      $accessCodeBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($accessCode))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          Body = @{
              'grant_type' = 'access_code'
              'assertion' = '{0}.{1}.' -f $algorithmBase64, $accessCodeBase64
              'state' = 'state_string'
          } | ConvertTo-Json
          Method = 'Post'
          URI = $uri
      }
      Invoke-RestMethod @params
      

      Example 1b – JSON request body and access code w/o Base64 encoding:

      $uri = 'https://192.168.110.90/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}\n"
      $accessCode = 'PasteAccessCodeFromAppliancePortalHere'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($algorithm))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          Body = @{
              'grant_type' = 'access_code'
              'assertion' = '{0}.{1}.' -f $algorithmBase64, $accessCode
              'state' = 'state_string'
          } | ConvertTo-Json
          Method = 'Post'
          URI = $uri
      }
      Invoke-RestMethod @params
      

      Example 2a – URI request body format and access code with Base64 encoding:

      $uri = 'https://192.168.110.90/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}\n"
      $accessCode = 'PasteAccessCodeFromAppliancePortalHere'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($algorithm))
      $accessCodeBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($accessCode))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          Body = 'grant_type=access_code&assertion={0}.{1}.&state=state_string' -f $algorithmBase64, 
              $accessCodeBase64
          Method = 'Post'
          URI = $uri
      }
      Invoke-RestMethod @params
      

      Example 2b – URI request body format and access code w/o Base64 encoding:

      $uri = 'https://192.168.110.90/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}\n"
      $accessCode = 'PasteAccessCodeFromAppliancePortalHere'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::Default.GetBytes($algorithm))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          Body = 'grant_type=access_code&assertion={0}.{1}.&state=state_string' -f $algorithmBase64, 
              $accessCode
          Method = 'Post'
          URI = $uri
      }
      
      Invoke-RestMethod @params
      
    • #52315
      Participant
      Topics: 0
      Replies: 2
      Points: 0
      Rank: Member

      @Daniel, thank you so much for all the new examples. I've been using the access code generated by the appliance the whole time. Thanks for checking. Unfortunately all of them returned the same 400 error. I think we're heading in the right direction though.

      As for the Zoom screen share offer...I am interested. What timezone are you in? I'm in GMT 10:00+.

    • #52319
      Participant
      Topics: 2
      Replies: 376
      Points: 0
      Rank: Member

      want to mention: access code in your python example is base64 encoded json string.
      may be you need to properly fill needed properties before base64 encode it ?

      PS D:\> $access_code="eyJhdWQiOiAiaHR0cHM6Ly90Yi1zZXJ2ZXJzaC9hcGkvY29tbW9uLzEuMC90b2tlbiIsICJpc3MiOiAiaHR0cHM6Ly90Yi1zZXJ2ZXJzaCIs
      ICJwcm4iOiAiYWRtaW4iLCAianRpIjogIjI2NjA5YzM0LWE1ZDAtNGZmZS05YWViLThjMjZhMzMxMTY1MiIsICJleHAiOiAiMCIsICJpYXQiOiAiMTQwMjY4MjQ5NSJ9"
      >>>
      PS D:\> [text.encoding]::utf8.getstring([Convert]::FromBase64String($access_code))
      {"aud": "https://tb-serversh/api/common/1.0/token", "iss": "https://tb-serversh", "prn": "admin", "jti": "26609c34-a5d0-4ffe-9aeb-8c26a3311652", "exp": "0", "iat": "1402682495"}
      

      btw, may be your problem is encoding ? as far as I see, server want utf8, may be you sent your strings in unicode?

    • #52322
      Participant
      Topics: 2
      Replies: 376
      Points: 0
      Rank: Member

      ... not unicode... its Default in Daniel's code

    • #52325
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      fyi, the post from Gareth Shipp is me. For some strange reason Powershell.org is now automatically logging me into the forums with what Outlook account I'm logged in with in another browser. I can no longer choose what email address to use when logging into the forums. Weird!

    • #52327
      Moderator
      Topics: 2
      Replies: 525
      Points: 24
      Team Member
      Rank: Member

      Gareth, no problem.

      I am GMT+7 for another week. Are you on LinkedIn, Twitter, Facebook or an open Slack team like DevOps Chat, hangsops or PowerShell? To connect up and schedule a Zoom session without sharing our email addresses here.

      My contact details:
      https://www.linkedin.com/in/krebsdaniel

      https://www.facebook.com/danielkrebs42
      Slack user name: dan1el42

      Cheers,
      Daniel

    • #52329
      Participant
      Topics: 24
      Replies: 111
      Points: 0
      Rank: Member

      I'm on the Powershell Slack. Should be online within 45 min. If you're on too then, great!

    • #52951
      Participant
      Topics: 0
      Replies: 2
      Points: 0
      Rank: Member

      @Daniel, thank you so much for the session today. You've solved the mystery!

      Fyi to everyone else....

      Powershell was not using the correct base64 encoding. By copying the base64 code from a Python we managed to make a REST call to the appliance.

      I've tried all the other types with [Convert]::ToBase64String([System.Text.Encoding]:: but still cannot match what Python produces. I'll have to investigate further.

      Final code below:

      $uri = 'https://mydevice/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}"
      $accessCode = 'myacesscode'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($algorithm))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          #Body = 'grant_type=access_code&assertion={0}.{1}.&state=state_string' -f $algorithmBase64,$accessCode
          Body = 'grant_type=access_code&assertion={0}.{1}.&state=state_string' -f 'eyJhbGciOiJub25lIn0K',$accessCode
          Method = 'Post'
          URI = $uri
      }
    • #52953
      Moderator
      Topics: 2
      Replies: 525
      Points: 24
      Team Member
      Rank: Member

      Thanks Gareth.

      I've figured out why PowerShell returned a different Base64 string for the algorithm soon after our call had ended. I've made a mistake and missed to convert all escape characters from Python to PowerShell.

      Wrong:
      $algorithm = "{`"alg`":`"none`"}\n"

      Correct:
      $algorithm = "{`"alg`":`"none`"}`n"

      $uri = 'https://mydevice/api/common/1.0/oauth/token'
      $algorithm = "{`"alg`":`"none`"}`n"
      $accessCode = 'myacesscode'
      
      $algorithmBase64 = [Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($algorithm))
      
      $params = @{
          ContentType = 'application/x-www-form-urlencoded'
          Headers = @{'accept'='application/json'}
          Body = 'grant_type=access_code&assertion={0}.{1}.&state=state_string' -f $algorithmBase64, $accessCode
          Method = 'Post'
          URI = $uri
      }
      
Viewing 16 reply threads
  • The topic ‘curl to Invoke-RestMethod conversion’ is closed to new replies.