SmartApp Webhook HTTP Signatures

Hi,

Like a few others in the community, I wrote my first SmartApp without using node.js. As I understand it, one of the reasons for moving away from groovy was to give developers the flexibility to use “any language” to implement the SmartApp. Any language that can parse the “lifecycles” and make calls using the API can be used. I chose PHP. Unfortunately, the documentation is too heavily reliant on node.js examples to properly promote the claim that writing SmartApps are agnostic of language. It would read better if there were more examples in a variety of languages (Python, PHP, raw curl, etc.) In fact, why not have all the examples coded in the various languages as is often the case in REST API documentations.

One section in particular that stands out to me is signing. The document says, “A full discussion of HTTP signature verification is beyond the scope of this document”. I don’t think we need a section to explain Draft 3, but at least go over what the app has to actually do to verify (again, with curl code, or PHP, or others). Instead, it is all being hidden in the node.js SDK, and the document points you to the Draft. That’s rough. After a bit of digging I found that it was actually very straight forward to do it. Here is my PHP code just in case others come across this post:

   $auth=explode(",",$_SERVER['HTTP_AUTHORIZATION']);
   foreach($auth as $value) {
      if(preg_match('/(\S*)="(.*)"/', $value, $matches)) {
         $auth_info[$matches[1]] = $matches[2];
      }
   }
   
   // get the public key
   $ch = curl_init();
   curl_setopt($ch, CURLOPT_URL, "$keyUrl".$auth_info['keyId']);
   curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
   $auth_key = curl_exec($ch);
   curl_close($ch);

   $auth_data="";
   $auth_data=$auth_data."(request-target): post ".$_SERVER['REQUEST_URI']."\n";
   $auth_data=$auth_data."digest: ".$_SERVER['HTTP_DIGEST']."\n";
   $auth_data=$auth_data."date: ".$_SERVER['HTTP_DATE'];

   // verify
   if(!openssl_verify($auth_data, base64_decode($auth_info['signature']), $auth_key, OPENSSL_ALGO_SHA256)) {
      // respond
      http_response_code(400);
      header("content-type: application/json");
      echo '{ "error" : "signature verification failed." }';

      exit();
   }

Of course, the code assumes that the headers section will always be

“(request-target) digest date”

and the algorithm is

“rsa-sha256”

I haven’t read anything anywhere in the documentation to say that this will consistently be so. Since that seems to be what was implemented in the SDK can we assume that it is safe to code signing in this way or do we really have to implement being able to respond to any permutation of headers and algorithms?

Thanks,
Jose

Hello, @josep1972
Thank you for sharing your experience, I’ll mention this to the internal team to see if we can get more info about it.

2 Likes

Has there been any more information about this?

The info about the authentication in the SmartApp can be reviewed in the libraries used by the SDK:

There are two types of signatures supported by the SDK: APP_RSA and ST_PADLOCK, in your integration, you would need to change the authentication process based on the type selected (there’s an endpoint for this in the API | SmartThings Developers). For example, the older APP_RSA type requires that the public key be deployed with the app.

@nayelyz

Thank you for getting back. I am not sure this answers my question, though. Do the smartapps that choose to use the signature method need to be able to implement all possible permutation of algorithms and header? If so, the documentation you point to specifically spells out all possible algorithms, but not all possible headers.

Or…will it always be

“(request-target) digest date”

and

“rsa-sha256”

If so, I believe that is perfectly legal and a valid implementation of the spec. Just trying to figure out how much you have to code for.

Thanks,
Jose

Sorry about that. So, the team mentioned that those are the ones expected for SmartApps. Currently, there are no plans to change that, so you don’t have to support all headers and algorithms.

Thank you. It would be good if the documentation would be updated to make that clearer.