this returns;
sed: 1: "‘N
": invalid command code ?
zsh: no such file or directory: s/n/n/
zsh: command not found: ta’
I’m running Catalina 10.15.7 on my Mac, and Rasbian Buster v10 on the RPi I’m trying to Preauth.
this returns;
sed: 1: "‘N
": invalid command code ?
zsh: no such file or directory: s/n/n/
zsh: command not found: ta’
I’m running Catalina 10.15.7 on my Mac, and Rasbian Buster v10 on the RPi I’m trying to Preauth.
This was after having re-ran the ./keygen-client
and
DEVICE_PUBLIC_KEY="$(cat keys-client-generated/public.key | sed -e :a -e 'N;s/\n/\\n/;ta')"
to format it in my environment variable.
Interesting. It looks like the sed syntax in this command is specific to GNU sed and does not work with the MacOS version of sed. I was able to get a similar error on my Big Sur machine.
If you have homebrew installed and the sed from there installed as “gsed” you can modify the command to use that instead.
Alternately it looks like using:
DEVICE_PUBLIC_KEY="$(cat keys-client-generated/public.key | awk 1 ORS='\\n')"
will work. Can you give that a shot?
Drew
That got the public key set to my DEVICE_PUBLIC_KEY
variable and running;
curl -H "Authorization: Bearer $JWT" -H "Content-Type: application/json" -X POST -d "{ \"identity_data\" : $DEVICE_IDENTITY_JSON_OBJECT_STRING, \"pubkey\" : \"$DEVICE_PUBLIC_KEY\" }" $MENDER_SERVER_URI/api/management/v2/devauth/devices
now adds the device to my Preauthorized devices in hosted Mender. Thanks for the support! Now I just have to add the formatted public key to my Google Cloud function and make sure the call from there works as well.
Awesome. I’ll submit a docs change with that fix.
Drew
So the code I posted earlier that makes the API call from my Firebase Function still fails to put the device in the Preauthoried column of my dashboard after having copy-pasted the formatted public key used in the curl
request that worked.
I’ve tried adding the public key to the call from my environment several different ways;
"identity_data": {
"sku": `"${deviceId}"`,
"sn": `"${sn}"`
},
"pubkey": `"${process.env.MENDER_PUBKEY}"`,
};
"identity_data": {
"sku": `'${deviceId}'`,
"sn": `'${sn}'`
},
"pubkey": `'${process.env.MENDER_PUBKEY}'`,
};
"identity_data": {
"sku": `${deviceId}`,
"sn": `${sn}`
},
"pubkey": `${process.env.MENDER_PUBKEY}`,
};
But they all return the response body: {"size":0, "timeout":0}
as seen in my earlier post.
Hi @bradw the only thing I see different from our JavaScript example code is that pubkey needs to be quoted as a string.
Drew
That’s what I was showing in my last post. I tried it the three ways shown there. Using string interpolation I pass it from my process.env
using single, double and no quotes. All three return the same result.
Sorry but I don’t know much about Javascript. Perhaps @mzedel can comment on that.
Does Firebase have any kind of logging so we can see exactly what the API call looks like on the wire?
Drew
Can you generate another screenshot with the full details of the preauth call? It looks like there is a twistout to show more details.
The point where it says “function execution took ms, finished with status ‘ok’” and “Function execution started” doesn’t expand with nay additional info, even though there is an expand arrow there. The same with where it says body: {"size": 0, "timeout:0)
Here is the other where it says menderPreauth
with the sensitive info blacked out;
I think that it may be related to the actual deployment of the function to FIrebase Cloud Functions.
Heyho @bradw,
just now I used your code to get devices accepted and it was pretty close to working, the issues I found were the auth header needs to have the Bearer
part in:
const headers = {
'Content-Type':'application/json',
'Accept':'application/json',
'Authorization':`Bearer ${process.env.MENDER_API_KEY}`
}
Apart from that node-fetch
didn’t stringify the body automatically so I had to change the request options to:
{
method: 'POST',
body: JSON.stringify(inputBody),
headers: headers
}
and finally, the response body will be empty if successful, with the headers containing the location of the newly created device:
.then(res => {
const deviceId = res.headers.get('Location').substring(res.headers.get('Location').indexOf('/') + 1);
console.log(`deviceId: ${deviceId}`);
return Promise.resolve(deviceId);
})
Looking at the docs I see that the bearer part in the header is missing there and the res.json()
is misleading there… While I fear this is due to the underlying docs generator we use, I’ll create a ticket to get this fixed!
I knew it had to be something simple like that. Thanks so much for looking into this for me. I’ll plug this all in to my code, give it a go and let you know how it works out.
When I update my code with what you’ve shown it now hits my catch()
block and returns Error: {}
.
Here is the code that produces Error:{}
from the catch()
block
const fetch = require('node-fetch');
const functions = require('firebase-functions');
module.exports = functions.pubsub.topic('menderPreauthorize').onPublish((message) => {
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}`
};
response = fetch ('https://hosted.mender.io/api/management/v2/devauth/devices',
{
method: 'POST',
body: JSON.stringify(inputBody),
headers: headers
})
.then(res => {
const deviceId = res.headers.get('Location').substring(res.headers.get('Location').indexOf('/') + 1);
console.log(`deviceId: ${deviceId}`);
return Promise.resolve(deviceId);
})
.catch((err) => {
console.log(`Error: ${JSON.stringify(err)}`)
return err
})
} else {
response = 'deviceId was null.'
}
return response
})
After some investigation I found this Stack Overflow post that notes node-fetch
not playing nicely with Firebase Functions
and is now suggesting axios
as a better alternative. I was also getting the {size:0, timeout:0}
and body
related errors some mention in the comments under the solution using node-fetch
.
Using axios
and the same MENDER_PUBKEY
and MENDER_API_KEY
as when I had successfully made the call using curl
, I updated my code to the following;
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;
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}`
};
response = axios({
method: 'post',
url: 'https://hosted.mender.io/api/management/v2/devauth/devices',
data: JSON.stringify(inputBody),
headers: headers
})
.then((res) => {
return res.data
})
.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 received, ${err.request}`)
} else {
console.log(`Error: ${err.message}`)
}
})
} else {
response = 'deviceId was null.'
console.log('deviceId was null')
}
return response
})
Now I receive a 401
error status code, Unauthorized
as shown here;
@drewmoseley I updated JWT
again as you show here, verified I could call the API via curl
, regenerated my MENDER_PUBKEY
and updated it and MENDER_API_KEY
accordingly in my Firebase Function’s environment variables and still get this 401 Unauthorized
error (which is at least a more descriptive error than I had been getting).
I’ve tried it with data
in lieu of body
in the axios
request per their docs and tried both with and without JSON.stringify()
around the inputBody
. Any other ideas?
@bradw can you please dump the full request and paste it here, removing the JWT token?