title: Use bash, Azure CLI and REST API to access CosmosDB - how to get token and hash right?
body: <p>My ultimate goal is to upload a document to an existing CosmosDB (SQL) instance using bash and Azure CLI. Trouble is: Azure CLI does <a href="https://docs.microsoft.com/en-us/cli/azure/cosmosdb?view=azure-cli-latest" rel="nofollow noreferrer">not offer a command to modify documents</a>.</p>
  
  <p>To work around this I looked into the <code>az rest</code> command and was hoping to call into CosmosDB's REST API to complete the task, but after hours of unsuccessful trying, I'm always getting the error:</p>
  
  <blockquote>
    <p>Unauthorized({"code":"Unauthorized","message":"The input authorization
    token can't serve the request. Please check that the expected payload
    is built as per the protocol, and check the key being used. Server
    used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020
    13:50:22 +0000\n\n'\r\nActivityId: ...,
    Microsoft.Azure.Documents.Common/2.10.0"})</p>
  </blockquote>
  
  <p>To keep it simple for now, I'm trying to list all my databases using the REST API, <a href="https://docs.microsoft.com/en-us/rest/api/cosmos-db/list-databases" rel="nofollow noreferrer">as described in the docs</a> and if that works, move on to actual document uploads.</p>
  
  <p>I'm also trying to follow the instructions presented <a href="https://docs.microsoft.com/rest/api/cosmos-db/access-control-on-cosmosdb-resources" rel="nofollow noreferrer">in the docs</a> on how to generate the authorization header.</p>
  
  <p>The request to get the list of DBs is using the format: <code>GET https://{databaseaccount}.documents.azure.com/dbs</code></p>
  
  <p>Here's my bash script with problems/questions highlighted. </p>
  
  <p>Part 1: Get an access token - <strong>question</strong>: is this the right token to begin with?</p>
  
  <pre><code>masterKey=$(az cosmosdb keys list --name MYDBINSTANCENAME --query primaryMasterKey --output tsv)
  </code></pre>
  
  <p>Part 2: Generate payload to hash - all content must be lowercase</p>
  
  <pre><code>verb="get"
  resourceType="dbs"
  resourceLink="dbs"
  now=$((date -uR) | tr '[A-Z]' '[a-z]')
  payload="$verb\n$resourceType\n$resourceLink\n$now\n\\n"
  </code></pre>
  
  <p>Part 3: Hash the payload - <strong>issue</strong>: the result of this hash is different from <a href="https://docs.microsoft.com/rest/api/cosmos-db/access-control-on-cosmosdb-resources#constructkeytoken" rel="nofollow noreferrer">what the sample code in C# builds</a>. So either one must be wrong but both result in the same error message.</p>
  
  <pre><code>hashedPayload=$(printf $payload | openssl dgst -sha256 -hmac $masterKey -binary)
  </code></pre>
  
  <p>Part 4: Create the required authentication string and convert to base 64 - <strong>question:</strong> is the base 64 encoding required for use with <code>az rest</code>?</p>
  
  <pre><code>authString="type=master&amp;ver=1.0&amp;sig=$hashedPayload" | base64
  </code></pre>
  
  <p>Part 5: create the headers string. This is using JSON notation because the blank separated approach does not work, <a href="https://docs.microsoft.com/cli/azure/reference-index?view=azure-cli-latest#az-rest-required-parameters" rel="nofollow noreferrer">although the docs state it should</a>.</p>
  
  <pre><code>headers="{\"x-ms-date\": \"$now\", \"x-ms-version\": \"2018-12-31\", \"x-ms-documentdb-isquery\": \"true\", \"Content-Type\": \"application/query+json\", \"Authorization\": \"$authString\"}"
  </code></pre>
  
  <p>Part 6: call the REST API</p>
  
  <pre><code>az rest --verbose -m get -u "https://MYDBINSTANCENAME.documents.azure.com:443/dbs" --headers $headers
  </code></pre>
  
  <p>Output:</p>
  
  <pre><code>Request URL: 'https://MYDBINSTANCENAME.documents.azure.com:443/dbs'
  Request method: 'GET'
  Request headers:
      'User-Agent': 'AZURECLI/2.4.0 (HOMEBREW)'
      'Accept-Encoding': 'gzip, deflate'
      'Accept': '*/*'
      'Connection': 'keep-alive'
      'x-ms-date': 'sat, 25 apr 2020 13:54:10 +0000'
      'x-ms-version': '2018-12-31'
      'x-ms-documentdb-isquery': 'true'
      'Content-Type': 'application/query+json'
      'Authorization': 'type%3dmaster%26ver%...'
      'x-ms-client-request-id': 'a55357fe-411c-4adf-9fd6-1a255e010cca'
      'CommandName': 'rest'
      'ParameterSetName': '--verbose -m -u --headers'
  Request body:
  None
  Response status: 401
  Response headers:
      'Transfer-Encoding': 'chunked'
      'Content-Type': 'application/json'
      'Content-Location': 'https://MYDBINSTANCENAME.documents.azure.com/dbs'
      'Server': 'Microsoft-HTTPAPI/2.0'
      'x-ms-activity-id': '9119f8bd-53d9-4a87-8aff-a887ec652fed'
      'Strict-Transport-Security': 'max-age=31536000'
      'x-ms-gatewayversion': 'version=2.10.0'
      'Date': 'Sat, 25 Apr 2020 13:54:11 GMT'
  Response content:
  {"code":"Unauthorized","message":"The input authorization token can't serve the request. Please check that the expected payload is built as per the protocol, and check the key being used. Server used the following payload to sign: 'get\ndbs\n\nsat, 25 apr 2020 13:54:10 +0000\n\n'\r\nActivityId: 9119f8bd-53d9-4a87-8aff-a887ec652fed, Microsoft.Azure.Documents.Common/2.10.0"}
  </code></pre>