|
/* |
|
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED |
|
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
|
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
DEALINGS IN THE SOFTWARE. |
|
*/ |
|
|
|
const axios = require('axios'); |
|
|
|
const getCert = (async (email, password) => { |
|
const ed = await import("@noble/ed25519"); |
|
|
|
const privKey = ed.utils.randomPrivateKey(); // Secure random private key |
|
const pubKey = await ed.getPublicKeyAsync(privKey); |
|
|
|
console.log("Privkey = " + Buffer.from(privKey).toString('base64')); |
|
console.log("Pubkey = " + Buffer.from(pubKey).toString('base64')); |
|
|
|
console.log(""); |
|
|
|
try { |
|
const authResponse = await axios.post('https://my.vanmoof.com/api/v8/authenticate', null, { |
|
headers: { |
|
'Authorization': "Basic "+Buffer.from(email+':'+password).toString('base64'), |
|
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819", |
|
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0", |
|
} |
|
}); |
|
|
|
const authToken = authResponse.data.token; |
|
|
|
const appTokenResponse = await axios.get('https://api.vanmoof-api.com/v8/getApplicationToken', { |
|
headers: { |
|
'Authorization': "Bearer "+authToken, |
|
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819", |
|
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0", |
|
} |
|
}); |
|
|
|
const appToken = appTokenResponse.data.token; |
|
|
|
const bikeDataResponse = await axios.get('https://my.vanmoof.com/api/v8/getCustomerData?includeBikeDetails', { |
|
headers: { |
|
'Authorization': "Bearer "+authToken, |
|
'Api-Key': "fcb38d47-f14b-30cf-843b-26283f6a5819", |
|
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0", |
|
} |
|
}); |
|
|
|
for (const bikeIdx in bikeDataResponse.data.data.bikes) { |
|
const bike = bikeDataResponse.data.data.bikes[bikeIdx]; |
|
|
|
console.log("Bike "+bike.name); |
|
console.log("Bike ID: "+ bike.bikeId); |
|
console.log("Frame number: "+ bike.frameNumber); |
|
console.log("Frame serial: "+ bike.frameSerial); |
|
|
|
if (bike.bleProfile === 'ELECTRIFIED_2022') { |
|
console.log("Bike is an SA5"); |
|
console.log("ECU Serial: "+ bike.mainEcuSerial); |
|
|
|
const certificateResponse = await axios.post('https://bikeapi.production.vanmoof.cloud/bikes/'+bike.bikeId+'/create_certificate', { |
|
'public_key': Buffer.from(pubKey).toString('base64'), |
|
}, { |
|
headers: { |
|
'Authorization': "Bearer "+appToken, |
|
'User-Agent': "VanMoof/20 CFNetwork/1404.0.5 Darwin/22.3.0", |
|
} |
|
}); |
|
|
|
console.log("Certificate below:") |
|
console.log("-----------"); |
|
console.log(certificateResponse.data); |
|
console.log("-----------"); |
|
} else { |
|
console.log("Not an SA5.") |
|
} |
|
} |
|
|
|
console.log(""); |
|
console.log(""); |
|
} catch (error) { |
|
console.log(error); |
|
} |
|
}); |
|
|
|
getCert("YOUR_VANMOOF_EMAIL", 'YOUR_VANMOOF_PASSWORD'); |
@magnusnordlander Yeah, that's probably it. I used
ssh-keygen -t ed25519
. Then appliedawk '{ printf $2 }' my_public_key.txt
, both with and without piping tobase64
. It was helpful to see your script applybase64
. I always mess up my encryption. That's why I appreciated your approach, for an integrated solution, verified by others than myself.curl
mainly spat out errors when not using the--data-urlencode
option, complaining about some malformed URL (probably the padding=
characters in base64). However, subsequent attempts using the correct syntax/options gave no result at all. Just nothing. And an exit code of0
. Figured it might also be some server ddos protection, preventing too many attempts in a short timeframe. Alternatively, replacingproduction
withtest
in the URL gave the same results (the IP address was also the same for bothtest
andproduction
, for what it's worth).Yes, I repeatedly saved the bike details and the successful private/public keys.
In my bike details JSON some other API calls were mentioned by the way. With a little adjusting I could get some other hashes, that I saved just to be sure. I think it was replacing
v8
byapi/v8
in the URL.About the translation issues: I only guessed that "failliet" translated to "bankrupt", couldn't come up with a better word. But I admit to not having it checked by Google Translate 🙂 (just thrilled that I finally got it working).
@gregmcd awesome! Being so focused on getting nodejs to work, it didn't even occur to me that
python
could be a great alternative. For old-fashioned people like me, that's a little less "exotic" than nodejs.Also new to the whole GitHub thing (used to GitLab), so I missed the notifications at first. Nice to see this remains an active topic.