Automating preauthorization using the API

const axios = require('axios');
const functions = require('firebase-functions');

module.exports = functions.pubsub.topic('menderPreauthorize').onPublish((messag=> {
  const deviceId = message.data ? Buffer.from(message.data, 'base64').toString() : null;
  let response
  if (deviceId) {
    const sn = deviceId.split('y')[1]
    const inputBody = {
      "identity_data": {
      "sku": deviceId,
      "sn": sn
    },
      "pubkey": process.env.MENDER_PUBKEY,
    };
    const headers = {
      'content-type':'application/json',
      'Accept':'application/json',
      'Authorization':`Bearer ${process.env.MENDER_API_KEY}` // JWT from $(curl -X POST -u $MENDER_SERVER_USER $MENDER_SERVER_URI/api/management/v1/useradm/auth/login)
    };
    response = axios({
      method: 'post',
      url: 'https://hosted.mender.io/api/management/v2/devauth/devices',
      body: JSON.stringify(inputBody), // I've tried data here as well per axios docs and both with and without JSON.stringify() 
      headers: headers
    })
    .then(res => {
        const id = res.headers.get('Location').substring(res.headers.get('Location').indexOf('/') + 1);
        console.log(`deviceId: ${id}`);
        return Promise.resolve(id);
    })
    .catch((err) => {
      if (err.response) {
        console.log(`error data: ${JSON.stringify(err.response.data)}`)
        console.log(`error status: ${err.response.status}`)
        console.log(`error headers: ${JSON.stringify(err.response.headers)}`)
      } else if (err.request) {
        console.log(`error response: Request was made but no response recieved, ${err.request}`)
      } else {
        console.log(`Error: ${err.message}`)
      }
   })
  } else {
    response = 'deviceId was null.'
    console.log('deviceId was null')
  }
  return response
})

This request works for me:

const axios = require('axios');

function test() {
  let response
    const inputBody = {
      "identity_data": {
        "sku": "001",
        "sn": "001"
      },
      "pubkey": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----\n"
    };
    const headers = {
      'Accept':'application/json',
      'Authorization':`Bearer ....`
    };
    response = axios({
      method: 'post',
      url: 'https://hosted.mender.io/api/management/v2/devauth/devices',
      data: inputBody,
      headers: headers
    })
    .then(res => {
        const id = res.headers.get('Location').substring(res.headers.get('Location').indexOf('/') + 1);
        console.log(`deviceId: ${id}`);
        return Promise.resolve(id);
    })
    .catch((err) => {
      if (err.response) {
        console.log(`error data: ${JSON.stringify(err.response.data)}`)
        console.log(`error status: ${err.response.status}`)
        console.log(`error headers: ${JSON.stringify(err.response.headers)}`)
      } else if (err.request) {
        console.log(`error response: Request was made but no response recieved, ${err.request}`)
      } else {
        console.log(`Error: ${err.message}`)
      }
   })
  return response
}

test()

@tranchitella @drewmoseley ,

I copy and pasted @tranchitella’s call to my Firebase Function, modifying it only where I grab the pubkey and Authorization I added some console.log just to be certain of what is being passed in from Firebase Functions’ process.env. I also made the call to the API successfully using curl with the JWT and pubkey I’m using here. I still get the same 401 Unauthorized error. See the modified code and Firebase log output below.

const axios = require('axios');
const functions = require('firebase-functions');

module.exports = functions.pubsub.topic('menderPreauthorize').onPublish((message) => {
  const deviceId = message.data ? Buffer.from(message.data, 'base64').toString() : null;
  console.log(`deviceId: ${deviceId}`)
  let response
  if (deviceId) {
    const sn = deviceId.split('y')[1]
    console.log(`sn: ${sn}`)
    console.log(`MENDER_PUBKEY typeof: ${typeof process.env.MENDER_PUBKEY}`)
    console.log(`MENDER_API_KEY typeof: ${typeof process.env.MENDER_API_KEY}`)
    const inputBody = {
      "identity_data": {
        "sku": deviceId,
        "sn": sn
      },
      "pubkey": process.env.MENDER_PUBKEY
    };
    const headers = {
      'Accept':'application/json',
      'Authorization': `Bearer ${process.env.MENDER_API_KEY}`
    };
    response = axios({
      method: 'post',
      url: 'https://hosted.mender.io/api/management/v2/devauth/devices',
      data: inputBody,
      headers: headers
    })
    .then(res => {
        const id = res.headers.get('Location').substring(res.headers.get('Location').indexOf('/') + 1);
        console.log(`deviceId: ${id}`);
        return Promise.resolve(id);
    })
    .catch((err) => {
      if (err.response) {
        console.log(`error data: ${JSON.stringify(err.response.data)}`)
        console.log(`error status: ${err.response.status}`)
        console.log(`error headers: ${JSON.stringify(err.response.headers)}`)
      } else if (err.request) {
        console.log(`error response: Request was made but no response recieved, ${err.request}`)
      } else {
        console.log(`Error: ${err.message}`)
      }
   })
 } else {
   response = null
 }
  return response
})

Sorry @bradw, I don’t know how to further help you dig into this issue. Does my code work for you locally? I’m not using process.env, I simply hard-coded the values in the script. I’m not a Javascript ninja, thus I don’t know if there can be any issue in getting those from env variables… If the very same code (excluding the “functions” and pubsub, which is firebase specific, works locally but not in firebase, then it must be something platform-specific.

I hard coded the MENDER_PUBKEY and MENDER_API_KEY in my Firebase Functions call and it worked. So it is clearly an issue with the way the variables are being brought in from process.env. I will reach out to Firebase support about it and if I don’t get a good answer there I’ll post a question on Stack Overflow.
Either way I’ll post the solution back here for anyone else who runs across this. Thanks for all the help!