I'm trying to use HERE's RouteMatching API of JavaScript
Current full code is here: https://github.com/code4history/ShibeContour/blob/d7e56a7/here_mapmatcher.js
For authentication, I coded like this:
import properties from "properties"
import hmacSHA256 from 'crypto-js/hmac-sha256.js'
import Base64 from 'crypto-js/enc-base64.js'
import fetch from 'node-fetch'
import {promises as fs} from "node:fs"
const getProps = async () => {
return new Promise((res) => {
properties.parse("./credentials.properties", {path: true}, function (error, data) {
res(data)
})
})
}
const getToken = async (props) => {
const nonce = `${performance.now()}`
const timestamp = Math.floor((new Date()).getTime() / 1000)
const parameters = [
"grant_type=client_credentials",
`oauth_consumer_key=${props["here.access.key.id"]}`,
`oauth_nonce=${nonce}`,
"oauth_signature_method=HMAC-SHA256",
`oauth_timestamp=${timestamp}`,
"oauth_version=1.0"
].join("&")
const encoding_params = encodeURIComponent(parameters)
const base_string = `POST&${encodeURIComponent(props["here.token.endpoint.url"])}&${encoding_params}`
console.log(base_string)
const signing_key = `${props["here.access.key.secret"]}&`
const hmac_digest = encodeURIComponent(Base64.stringify(hmacSHA256(base_string, signing_key)))
const headers = {
"Authorization": `OAuth oauth_consumer_key="${props["here.access.key.id"]}",oauth_nonce="${nonce}",oauth_signature="${hmac_digest}",oauth_signature_method="HMAC-SHA256",oauth_timestamp="${timestamp}",oauth_version="1.0"`,
"Cache-Control": "no-cache",
"Content-Type": "application/x-www-form-urlencoded"
}
const body = `grant_type=client_credentials`
const response = await fetch(props["here.token.endpoint.url"], {
method: 'post',
body,
headers
})
return response.json()
}
This works well, I got authentication token successfully.
Like this:
{
access_token: 'eyJhbGciOiJSUzUxMiIsImN0eSI6IkpXVCIsImlzcyI6IkhFUkUiLCJhaWQiOiJIZ0NSaFV4...',
token_type: 'bearer',
expires_in: 86399,
scope: 'hrn:here:authorization::org...'
}
But even I used this access_token, route matching call causes authentication error.
Code is:
const main = async () => {
const props = await getProps()
const token_data = await getToken(props)
const body = await fs.readFile("gps/8DD83AC3-8B5A-4108-9CC0-2B78CF9936EC.kml", {encoding: "UTF-8"})
const headers = {
"Authorization": `Bearer ${token_data.access_token}`,
"Cache-Control": "no-cache",
"Content-Type": "application/octet-stream"
}
const response = await fetch(`https://routematching.hereapi.com/v8/calculateroute.json?routeMatch=1&mode=fastest;car;traffic:disabled&apiKey=${props["here.access.key.id"]}`, {
method: 'post',
body,
headers
})
const respond = await response.json()
console.log(respond)
}
main()
Error response was like this:
{
error: 'Forbidden',
error_description: 'These credentials do not authorize access'
}
What is wrong?
I can't imagine what is wrong.
Finally I found the reason
API URL is not match.
We can find many candidate urls,
https://fleet.api.here.com/2/calculateroute.json
https://routematching.hereapi.com/v8/calculateroute.json
etc...
but true working url is only
https://routematching.hereapi.com/v8/match/routelinks
which we can find in this document.
https://platform.here.com/services/details/hrn:here:service::olp-here:route-matching-8/api-ref
Once I changed API endpoint to this correct one, it works well.
Related
I am trying to convert a Python app I made some years ago to a (better) NodeJS implementation. The function in question obtains an access token from the Twitter api to attach to future requests, but my implementation returns 403 bad request. Here is a the functional Python implementation..
def get_bearer_header():
uri_token_endpoint = 'https://api.twitter.com/oauth2/token'
key_secret = f"{twitter_creds.CONSUMER_KEY}:{twitter_creds.CONSUMER_KEY_SECRET}".encode('ascii')
b64_encoded_key = base64.b64encode(key_secret)
b64_encoded_key = b64_encoded_key.decode('ascii')
auth_headers = {
'Authorization': 'Basic {}'.format(b64_encoded_key),
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
auth_data = {
'grant_type': 'client_credentials'
}
auth_resp = requests.post(uri_token_endpoint, headers=auth_headers, data=auth_data)
bearer_token = auth_resp.json()['access_token']
bearer_header = {
'Accept-Encoding': 'gzip',
'Authorization': 'Bearer {}'.format(bearer_token),
'oauth_consumer_key': twitter_creds.CONSUMER_KEY
}
return bearer_header
and here is the JS implementation so far..
export const getBearerHeader = async () => {
const keyAndSecret = btoa(`${config.twitterApi.consumerKey}:${config.twitterApi.consumerKeySecret}`)
const buff = Buffer.from(keyAndSecret, 'utf-8');
const b64KeyAndSecret = buff.toString('base64');
const body = new URLSearchParams({
'grant_type': 'client_credentials'
})
const url = config.twitterApi.oauthTokenEndpoint
const headers = {
'Authorization': `Basic ${b64KeyAndSecret}`,
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
}
const resp = await axios.post(url, body, headers)
console.log("RESPONSE.data")
console.log(resp.data)
}
the request looks fine according to twitter docs, but my response says 403!
Any assistance appreciated!
Get Twitter access token v2 by python and node.js code.
To create API Key and Secret from
Twitter Developer Portal
Main idea to use curl commend for getting access token.
POST oauth2/token
Using config.json file
{
"API_KEY" : "Xx7MxxxxxTkzxxxxxq9xxxxxQ",
"API_KEY_SECRET" : "om4QxxxxxPrcPlxxxxxFikDxxxxxoC5mQxxxxxLy7M17xxxxxK"
}
Node.js code
const axios = require('axios')
const config = require('./config.json');
const getAccessToken = async () => {
try {
const resp = await axios.post(
'https://api.twitter.com/oauth2/token',
'',
{
params: {
'grant_type': 'client_credentials'
},
auth: {
username: config.API_KEY,
password: config.API_KEY_SECRET
}
}
);
console.log(resp.data);
} catch (err) {
// Handle Error Here
console.error(err);
}
};
getAccessToken();
python code
import requests
import json
import ast
def get_access_token():
with open('config.json') as config_file:
data = json.load(config_file)
params = {
'grant_type': 'client_credentials',
}
response = requests.post('https://api.twitter.com/oauth2/token', params=params, auth=(data['API_KEY'], data['API_KEY_SECRET']))
print(response.content)
json_data = ast.literal_eval(response.content.decode("UTF-8"))
return json_data['access_token']
print(get_access_token())
run from terminal
Whenever I am trying to invoke the "btoa" method, I am not able to use this within my script. I created a variable to store the client id: client_secret in base64. The id and secrets are being retrieved from the ".env" file.
I have also tried to use the Buffer method, but unable to use this as well. I am getting the error "invalid from" in Buffer.
can someone help me?
Please look at the full code,
const client_id = process.env.SPOTIFY_CLIENT_ID;
const client_secret = process.env.SPOTIFY_CLIENT_SECRET;
const refresh_token = process.env.SPOTIFY_REFRESH_TOKEN;
const basic = btoa(`${client_id}:${client_secret}`);
const NOW_PLAYING_ENDPOINT = `https://api.spotify.com/v1/me/player/currently-playing`;
const TOP_TRACKS_ENDPOINT = `https://api.spotify.com/v1/me/top/tracks`;
const TOKEN_ENDPOINT = `https://accounts.spotify.com/api/token`;
const getAccessToken = async () => {
const response = await fetch(TOKEN_ENDPOINT, {
method: 'POST',
headers: {
Authorization: `Basic ${basic}`,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({
grant_type: 'refresh_token',
refresh_token
})
});
return response.json();
};
export const getNowPlaying = async () => {
const { access_token } = await getAccessToken();
return fetch(NOW_PLAYING_ENDPOINT, {
headers: {
Authorization: `Bearer ${access_token}`
}
});
};
export const getTopTracks = async () => {
const { access_token } = await getAccessToken();
return fetch(TOP_TRACKS_ENDPOINT, {
headers: {
Authorization: `Bearer ${access_token}`
}
});
};
Using the above script I am trying to embed the customized Spotify play on my site. This wrapper is intended to display the top track as well.
Also, whenever I am trying to run the wrapper used to display the top tracks, it displays the following error,
Full code for displaying the top tracks:
import { type NextRequest } from 'next/server';
import { getTopTracks } from 'lib/spotify';
export const config = {
runtime: 'experimental-edge'
};
export default async function handler(req: NextRequest) {
const response = await getTopTracks();
const { items } = await response.json();
const tracks = items.slice(0, 10).map((track) => ({
artist: track.artists.map((_artist) => _artist.name).join(', '),
songUrl: track.external_urls.spotify,
title: track.name
}));
return new Response(JSON.stringify({ tracks }), {
status: 200,
headers: {
'content-type': 'application/json',
'cache-control': 'public, s-maxage=86400, stale-while-revalidate=43200'
}
});
}
The problem is that you misspelled the Bytes to ASCII function, it is btoa, not btao.
If you are looking to do it the other way around, spell it atob.
I was always using plain JS und fetch call to handle API, they are working fine, now I want to replace all fetch with axios, so far the axios get calls are all working, but the axios post call are not, please guide:
the original fetch post from client side is like this, i need to send 3 values to server
const postTrip = async(location = '', daysToGo = '', notes ='') => {
const res = await fetch('http://localhost:7777/addData', {
method: 'POST',
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({location, daysToGo, notes}),
})
if (res.status >= 400 && res.status < 600) {
throw new Error("Bad response from server");
}
updateUI(daysToGo);
}
from server side
app.post('/addData', async (req, res) => {
try {
const city = req.body.location;
const dayLength = req.body.daysToGo;
const memo = req.body.notes;
let geo = await getGeo(city);
let weather = await getWeather(geo.lat, geo.lng, dayLength);
let image = await getImage(city);
const newEntry = {
geo,
weather,
image,
memo
}
projectData = newEntry;
res.status(201).send(projectData);
now I try to change client fetch post to axios post like this
const postTrip = async(location = '', daysToGo = '', notes ='') => {
const res = await axios('http://localhost:7777/addData', {
method: 'POST',
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
data: JSON.stringify({location, daysToGo, notes}),
})
if (res.status >= 400 && res.status < 600) {
throw new Error("Bad response from server");
}
updateUI(daysToGo);
}
and change the server side like this:
app.post('/addData', async (req, res) => {
try {
const city = req.data.location;
const dayLength = req.data.daysToGo;
const memo = req.data.notes;
let geo = await getGeo(city);
let weather = await getWeather(geo.lat, geo.lng, dayLength);
let image = await getImage(city);
const newEntry = {
geo,
weather,
image,
memo
}
projectData = newEntry;
res.status(201).send(projectData);
it dosen't work, the server didn't get the three values from front, where is wrong?
With axios you don't have to stringify the body.
const postTrip = async(location = '', daysToGo = '', notes ='') => {
const res = await axios('http://localhost:7777/addData', {
method: 'POST',
mode: "cors",
cache: "no-cache",
credentials: "same-origin",
headers: {
"Content-Type": "application/json"
},
data: {location, daysToGo, notes},
})
if (res.status >= 400 && res.status < 600) {
throw new Error("Bad response from server");
}
updateUI(daysToGo);
}
I am building a comments section onto a Node/Express app for family reunions. I first wrote it all on the server side, but then ran into the issue where I was unable to update the DOM after posting the comment without refreshing the page.
My research yielded that I could use AJAX or the fetch API to do this, client-side.
I'm using some client-side JavaScript to post comments. I have a route for the POST request:
router.post('/:reunionId', isAuth, reunionController.postComment);
The controller code is:
exports.postComment = (req, res, next) => {
const commentText = req.body.newComment;
const reunionId = req.body.reunionId;
const foundReunion = Reunion.findById(reunionId)
.populate({
path: 'comments',
options: { sort: { createdAt: -1 } },
})
.then((reunion) => {
console.log(reunion);
const comment = new Comment({
_id: new mongoose.Types.ObjectId(),
text: commentText,
reunionId: new mongoose.Types.ObjectId(reunionId),
userId: req.user._id,
});
foundReunion.comments.push(comment);
comment.save();
foundReunion.save();
console.log('Operation completed successfully');
return foundReunion;
})
.catch((error) => {
const newError = new Error(error);
newError.httpStatusCode = 500;
return next(newError);
});
};
And the client-side code:
const commentForm = document.getElementById('comment-form');
const commentInput = document.getElementById('newComment');
const commentsContainer = document.getElementById('allComments');
let commentText = document.getElementById('newComment').value;
const reunionId = document.getElementById('reunionId').value;
const csrfToken = document.getElementById('csrf').value;
commentForm.addEventListener('submit', handleCommentSubmit, false);
commentInput.addEventListener('change', (event) => {
commentText = event.target.value;
});
async function handleCommentSubmit(event) {
event.preventDefault();
console.log('Someone clicked the comment submit button...');
console.log(csrfToken); // This works.
console.log(reunionId); // This works.
console.log(commentText); // This works.
const url = `http://localhost:3006/reunions/${reunionId}`;
fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'X-CSRF-Token': csrfToken,
},
body: { // This is not working.
reunionId,
commentText,
},
})
.then((response) => {
const d = response.comment.createdAt.getDate();
const m = monthNames[response.comment.createdAt.getMonth()];
const y = response.comment.createdAt.getFullYear();
const commentDiv = document.createElement('div');
commentDiv.classList.add('comments-container');
const commentP = doucment.createElement('p');
commentP.classList.add('comment-header-text');
const email = response.comment.userId.email;
const hr = document.createElement('hr');
commentP.textContent = `On ${m}+ ' ' +${d}+ ', ' +${y}, ${email} wrote:`;
commentDiv.appendChild(commentP);
commentDiv.appendChild(commentText);
commentDiv.appendChild(hr);
commentsContainer.appendChild(commentDiv);
})
.catch((error) => console.log(error));
The client makes the POST request, properly passes the csrf token, but the server cannot read the reunionId or commentText from the body of the request. I get Reunion.findOne({ null }) in the server logs.
I am simply not sure what Content-Type to declare, whether I need to at all, or how to pass the two pieces of data I need in the body of the call to fetch.
Thanks very much in advance.
The body of a post must always be a string. What you are missing is you need to JSON.strigify your object and them make add the content-type header to specify that the body is application/json:
fetch(url, {
method: 'POST',
credentials: 'include',
headers: {
'X-CSRF-Token': csrfToken,
'Content-Type': 'application/json'
},
body: JSON.stringify({
reunionId,
commentText,
}),
})
I'm trying to integrate Auth0.com, and I've written a function that should unlink an identity and then delete it.
The unlinking works, but I keep getting a 403 error when trying to delete the unlinked identity.
The Auth0 API docs (https://auth0.com/docs/api/management/v2#!/Users/delete_users_by_id) say a 403 error is due to either rate limits, insufficient scopes, or user not matching the bearer token.
I think I added the correct scopes to the auth0 client, I'm very sure I'm not hitting rate limits, so it must be the mismatching bearer token.
But I don't understand how that could be?
Can you take a look at what I have and tell me what's gone wrong?
P.S. In case it matters, I'm using auth0-spa.js not the standard auth0.js. More info here.
This is the code I'm working with:
function(properties, context) {
// Load any data
const domain = context.keys.auth0_domain;
const client_id = context.keys.auth0_client_id;
const connection = properties.connection;
const auth0_user_id = properties.auth0_user_id;
//Do the operation
const auth0 = new Auth0Client({
domain: domain,
client_id: client_id,
audience: `https://${domain}/api/v2/`,
scope: "openid email profile read:current_user update:current_user_identities delete:users delete:current_user",
});
const auth0_user_obj = {
id: auth0_user_id
};
const getUserProfile = async (userId) => {
const token = await auth0.getTokenSilently();
const response = await fetch(
`https://${domain}/api/v2/users/${userId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
}
);
return await response.json();
};
const getSecondaryIdentity = async () => {
const auth0user = await getUserProfile(auth0_user_id);
const secondary_identity = auth0user.identities.find(i => i.connection === connection);
return secondary_identity;
}
const unlinkDeleteAccount = async () => {
const secondaryIdentityObj = await getSecondaryIdentity();
const {
provider,
user_id
} = secondaryIdentityObj;
const accessToken = await auth0.getTokenSilently();
const {
sub
} = await auth0.getUser();
await fetch(
`https://${domain}/api/v2/users/${sub}/identities/${provider}/${user_id}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
const secondUserId = provider + '|' + user_id;
await fetch(
`https://${domain}/api/v2/users/${secondUserId}`, {
method: "DELETE",
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
};
unlinkDeleteAccount();
}