Problem with http-signature.verifySignature in Azure Functions

I’m new to developing on the SmartThings platform. I’d like to make an Azure Functions version of https://github.com/SmartThingsCommunity/weather-color-light-smartapp-nodejs

While moving it from an Express app to an Azure Function, I’m having a problem with my httpSignature.verifySignature(parsed, publicKey) statements. They return true in Express, but false my Azure Functions.

Any ideas / suggestions on how to fix this?

// Express App
/**

  • Sample WebHook app for integrating with the SmartThings API.
  • The app will set the color of a SmartThings-connected Color Control bulb
  • according to the current weather conditions of a user-entered US five-digit
  • Zip Code.
    */
    ‘use strict’;
    const express = require(‘express’);
    const bodyParser = require(‘body-parser’);
    const request = require(‘request’);
    const fs = require(‘fs’);
    const httpSignature = require(‘http-signature’);
    const prettyjson = require(‘prettyjson’);
    const commands = require(’./lib/commands’);
    const stConfig = require(’./lib/config’);
    const scheduling = require(’./lib/scheduling’);
    const weather = require(’./lib/weather’);
    const stApi = ‘https://api.smartthings.com/v1’;
    const prettyjsonOptions = {};
    const app = express();
    app.use(bodyParser.json());

const publicKey = fs.readFileSync(’./config/smartthings_rsa.pub’, ‘utf8’);
/**

  • Entry point for callbacks by SmartThings.
  • Every incoming call will have a lifecycle, which determines how the
  • app should respond.
  • All requests will have their HTTP signature verified to ensure the
  • request is actually from SmartThings, except for the PING lifecycle
  • request (which occurs as the app is being created).
    */
    app.post(’/’, function (req, response) {
    // We don’t yet have the public key during PING (when the app is created),
    // so no need to verify the signature. All other requests are verified.
    if (req.body && req.body.lifecycle === “PING” || signatureIsVerified(req)) {
    handleRequest(req, response);
    } else {
    response.status(401).send(“Forbidden”);
    }
    });

/**

  • Verifies that the request is actually from SmartThings.

  • @returns true if verified, false otherwise.
    */
    function signatureIsVerified(req) {

    try {
    let parsed = httpSignature.parseRequest(req);

    //console.log(JSON.stringify(parsed,null,4));

    if (!httpSignature.verifySignature(parsed, publicKey)) {
    console.log(‘forbidden - failed verifySignature’);
    return false;
    }
    } catch (error) {
    console.error(error);
    return false;
    }
    return true;
    }
    // added port to read from Azure Services
    const port=process.env.PORT || 3005
    let server = app.listen(port);
    module.exports = server;
    console.log(‘Open: http://127.0.0.1:’ + port);


// Azure Function

/**

  • Sample WebHook App for integrating with the SmartThings API.

  • The app will set the color of a SmartThings-connected Color Control bulb

  • according to the current weather conditions of a user-entered US five-digit

  • Zip Code.
    /
    ‘use strict’;
    const weatherforecast = require(’./weatherforecast’);
    const fs = require(‘fs’);
    const httpSignature = require(‘http-signature’);
    const publicKey = fs.readFileSync(’./smartthings_rsa.pub’, ‘utf8’);
    /
    *

  • All requests will have their HTTP signature verified to ensure the

  • request is actually from SmartThings, except for the PING lifecycle

  • request (which occurs as the app is being created).
    */
    module.exports = async function (context, req) {
    console.log(‘Weather Forecast function processed a request.’);

    if (req.body && req.body.lifecycle === “PING”) {
    weatherforecast.handleRequest(context.req, context.res);
    } else {
    // Check signature
    let parsed = httpSignature.parseRequest(context.req);
    //console.log(“Full Req:”);
    //console.log(JSON.stringify(parsed,null,4));
    try {
    if (!httpSignature.verifySignature(parsed, publicKey)) {

              console.log('forbidden - failed verifySignature');
              context.res.status(401).send("Forbidden");
      
          } else {
              // OK - good to go!
              weatherforecast.handleRequest(context.req, context.res);
      
          }
      } catch (error) {
          console.error(error);
          context.res.status(401).send("Forbidden");
      }
    

    }
    };

Have a look at https://github.com/ianisms/SmartThings.NETCoreWebHookSDK/blob/master/src/SmartThings.NETCoreWebHookSDK/Crypto/CryptoUtils.cs, using BouncyCastle…

Thanks Ian. I was able to get it working. There is a difference in context.req in an Azure function and req in Express. context.req contains the full url, but req does not include the hostname. So, the easy fix is to remove the hostname before calling parseRequest().

// context.req includes hostname; so remove it
var tempURL = new URL(context.req.url);
context.req.url = tempURL.pathname;

// Check signature
let parsed = httpSignature.parseRequest(context.req);

Thank you!

1 Like

© 2019 SmartThings, Inc. All Rights Reserved. Terms of Use | Privacy Policy

SmartThings; SmartApps®; Physical Graph; Hello, Home; and Hello, Smart Home are all trademarks of the SmartThings, Inc.