“Unauthorized” response when cityiq API - javascript

I am trying to access the Current powered by GE CityIQ API to develop a parking app, I followed the API documentation however I cannot seem to successfully query because I do not have an access token. I have a user name and password as well as the urls and predix zone id for parking provided by the city I am using. When I try and run my javascript and log my access token the response is “Unauthorized”. Do i have to raise a request to the city for the access token?
The code is written in javascript and is using node.js and node-fetch.
Here is my code:
const fetch = require("node-fetch")
function request(url, headers, body) {
let options = { headers: headers, body:body}
return fetch(url, options).then(result => {
if (result.status>=400) return(result.statusText)
else return result.text().then(txt => {
try { return JSON.parse(txt) }
catch (err) { return txt }
})
})
}
// my credentials
const developer, uaa, metadataservice, eventservice, predixZone
developer = '{user}:{pass}'
uaa='{uaaURL}'
eventservice='{eventURL}'
metadataservice='{metadataURL}'
predixZone='{predixzoneParking}'
async function example(event){
let devToken = (await request(uaa+'?grant_type=client_credentials', {authorization: 'Basic '+developer}))
console.log(devToken)
let output = (await request(metadataservice+'/assets/search?q=eventTypes:PKIN',{authorization: 'Bearer '+devToken,'predix-zone-id':predixZone})).content
console.log(output)
}
example()
What am I doing wrong or probably missing?

It looks like you have not base64 encoded your username and password.
At the top of your code:
const btoa = str => new Buffer(str).toString('base64')
When you declare your user name and pass:
developer = btoa('{user}:{pass}')

Related

Is it possible to access token in service-worker fetch request headers?

I want to send a GET request including a token "Authorization" header to my nodejs server. For this, I use this function client-side:
// Do a secured GET API request and return response object
async function getJSON(url) {
try {
// Send request
const res = await fetch(url, {
headers: { Authorization: localStorage.getItem('token') }
});
// If something went wrong
if (!res.ok) {
// If token is invalid
if (res.status === 401) {
// Logout
localStorage.removeItem('token');
location.replace(`/?msg=${dict.get('expired-session')}`);
}
// Return an object with error message and status code
return { error: res.statusText, status: res.status };
}
// Else return response object
else return await res.json();
} catch (err) {
// Return an object with error message
return { error: err.message };
}
}
It works fine, but I use a service worker to cache requests as they are made and I don't want API requests to be cached, so I though I could just check for the "Authorization"'s presence:
// Fetch event
self.addEventListener('fetch', e => e.respondWith(respond(e)));
async function fetchAndCache(req, cache_name) {
const { url, headers } = req;
console.log({ url, headers });
// Fetch request
const fetch_res = await fetch(req);
const is_get = req.method === 'GET';
const is_api = req.headers.Authorization;
const is_cahing_domain = cache_domains.some(domain => req.url.includes(domain));
if (is_cahing_domain && is_get && !is_api) {
// Open cache and save a cloned result
const cache = await caches.open(cache_name);
cache.put(req, fetch_res.clone());
}
return fetch_res;
}
async function respond(e) {
if (!use_cache) return fetch(e.request);
// Try to get response from cache
const cached_res = await caches.match(e.request);
// If response is found, return it
if (cached_res) return cached_res;
// If request is not found, try to fetch it
return await fetchAndCache(e.request, 'main');
}
Unfortunately, the logs show empty headers:
Even though the server does get the token and the cached (since the condition does not work) request also includes it:
I searched for a few hours, tried every solutions in similar questions (here and here) but none worked. Please help.
I don't know about Authorization header. But the right way to print headers is via method 2. It seems like you are using method 1.
console.log("Try 1 to print headers.");
console.log(request.headers);
console.log("Try 2 to print headers.")
for (const pair of event.request.headers.entries()) {
console.log(pair[0]+ ': '+ pair[1]);
}
Console logs
Headers {} does not mean that the header is empty. It means that it contains an object which is not printed in detail in your console.
You can access the Authorization header as follow:
console.log('AuthorizationHeader is:',headers.get('Authorization'))

Generic fetch replay to handle token refresh

I am looking for a generic way to replay a fetch if I get a 401 response.
My application is a SPA using OIDC. Our frontend developers utilise fetch, and a ServieWorker injects the access_token into the AJAX request before sending the request to the API(s). There are times when a fetch occurs but access_token is expired. When that happens, I want to use the refresh_token to get a new access_token and then replay the fetch, returning the replayed fetch in the Promise. Ideally, this would be something the frontend developers would not even know is happening.
Meaning that a UI developer will code something like what's below (remember, the access_token is injected via ServiceWorker):
fetch("https://backend.api/user/get/1")
.then(resp =>
{
console.log("user information is XYZ. Raw response:", resp);
})
When really what's happening in the background is:
[Initial request] > [Expired token response] > [Request new token] > [Initial request replayed]
I've experimented with overriding the fetch method with what's below, but I can't figure out a generic way to recreate/clone the original fetch:
window.fetch = new Proxy(window.fetch, {
apply(fetch, that, args) {
// Forward function call to the original fetch
const result = fetch.apply(that, args);
// Do whatever you want with the resulting Promise
result.then(resp =>
{
if(resp.status == 400 || resp.status == 401)
{
let rt = getRefreshToken();
return fetch("https://idaas.provider/get/new/token", {
"method": "POST",
"body": new URLSearchParams({
grant_type: "refresh_token",
refresh_token: rt,
client_id: client_id_str
})
});
}
}).then(resp =>
{
let new_token = resp.new_token;
send_new_token_to_service_worker(new_token);
return new_token
}).then(tok =>
{
// How do I replay the original request?
})
return result;
}
});
The goal is to simplify what the UI developers need to handle. I want them focused on UX and have this sort of error handling done in the background.
Note: If necessary, I would be open to not using fetch and instead utilising a wrapper method. Obviously, because of the code already written surrounding fetch, the new method would need to accept the same arguments and produce the same return.
window.fetch = new Proxy(window.fetch, {
apply(fetch, that, args) {
let fetchApi = [
fetch(args.shift())
];
let fetchToken = [
fetch("https://idaas.provider/get/new/token", {
method: "POST",
body: new URLSearchParams({
grant_type: "refresh_token",
refresh_token: getRefreshToken(),
client_id: client_id_str,
})
}).then(token => send_new_token_to_service_worker(token)),
];
return Promise.allSettled(fetchApi).then(results => {
let rejected = results
.map(result => result.status)
.includes("rejected");
if (rejected) {
return Promise.all([...fetchToken, ...fetchApi]).then(results => results.at(1));
} else {
return results.at(0).value;
}
});
},
});
Usage fetch("https://your-api").then(resp => resp);

How to prevent async - await freezing in javascript?

Good day I have a custom adonisjs command that pulls from an API.
async handle (args, options) {
// Status
// Open = 1979
// Get all jobs with open status.
const pullJobController = new PullJobsFromJobAdderController;
let token = await pullJobController.get_token();
if(token){
const jobs = await this._getOpenJobs('https://jobs/open-jobs', token , 1979);
}
}
async _getOpenJobs(url, accessToken, status) {
url = url + '?statusId=' + status
const headers = {
'Authorization': 'Bearer ' + accessToken
}
const options = {
method: 'GET',
url: url,
headers: headers
}
return (await rp(options).then(function (result) {
return {
status: true,
info: JSON.parse(result)
}
}).catch(function (error) {
return {
status: false
}
}));
} // _getOpenJobs()
PullJobsFromJobAdderController
async get_token()
{
// This works if directly returning the token.
// return "9ade34acxxa4265fxx4b5x6ss7fs61ez";
const settings = await this.settings();
const jobAdderObject = new this.JobAdder(settings.jobadder['client.id'], settings.jobadder['client.secret'])
const jobadderOauthObject = this.model('JobadderOauth');
const accessInfo = await jobadderOauthObject.jobdderLatestAccess();
let isAccessExpired = await this.checkAccessValidity(accessInfo.created_at);
let accessToken = accessInfo.access_token;
let apiEndpoint = accessInfo.api_endpoint;
if(isAccessExpired === true){
let refreshTokenInfo = await jobAdderObject.refrehToken(accessInfo.refresh_token)
if (refreshTokenInfo.status === true) {
let refreshTokenDetails = JSON.parse(refreshTokenInfo.info)
accessToken = refreshTokenDetails.access_token
apiEndpoint = refreshTokenDetails.api
await jobadderOauthObject.create({
code: accessInfo.code,
access_token: refreshTokenDetails.access_token,
refresh_token: refreshTokenDetails.refresh_token,
scope: 'read write offline_access',
api_endpoint: refreshTokenDetails.api
})
}
}
return accessToken;
} // get_token()
The function async get_token works as expected, it supplies me with a fresh token to be used by the adonisjs command. However it freezes after running the command.
But if I return the string token directly. The custom command handle() works as expected and terminates after running.
Scenario 1: (Directly returning the token string from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected displaying in the terminal the result of the pulled data from the api.
Terminal is ready to accept another command.
Scenario 2: (Comment out the directly returned string token from PullJobsFromJobAdderController)
I run my custom command "adonis pull:jobs" and it runs as expected
displaying in the terminal the result of the pulled data from the
api.
Terminal is not accepting commands until I press ctrl+c and terminate the current job/command.
Perhaps I am missing something regarding async await calls.
Can someone point / help me to the right direction?
TIA
I got it, for anyone else having this kind of problem with adonis commands:
wrap the task inside your handle in a try... catch block then always have Database.close() and process.exit() in finally.

In firebase - How to generate an idToken on the server for testing purposes?

I want to test a a cloud function that creates users.
In normal cases, inside the browser i generate an idToken and i send it to server via headers: Authorization : Bearer etcIdToken
But I want to test this function without the browser. In my mocha tests i have:
before(done => {
firebase = require firebase.. -- this is suppose to be like the browser lib.
admin = require admin..
idToken = null;
uid = "AY8HrgYIeuQswolbLl53pjdJw8b2";
admin.auth()
.createCustomToken(uid) -- admin creates a customToken
.then(customToken => {
return firebase.auth() -- this is like browser code. customToken get's passed to the browser.
.signInWithCustomToken(customToken) -- browser signs in.
.then(signedInUser => firebase.auth() -- now i want to get an idToken. But this gives me an error.
.currentUser.getIdToken())
})
.then(idToken_ => {
idToken = idToken_
done();
})
.catch(err => done(err));
})
The error i'm getting is:
firebase.auth(...).currentUser.getIdToken is not a function - getting the idToken like this works on client - and is documented here.
I tried directly with signedInUser.getIdToken(). Same problem:
signedInUser.getIdToken is not a function - not documented. just a test.
I think this is because firebase object is not intended for node.js use like i'm doing here. When signing in - stuff get's saved in browser local storage - and maybe this is why.
But the question still remains. How can i get an idToken inside node.js in order to be able to test:
return chai.request(myFunctions.manageUsers)
.post("/create")
.set("Authorization", "Bearer " + idToken) --- i need the idToken here - like would be if i'm getting it from the browser.
.send({
displayName: "jony",
email: "jony#gmail.com",
password: "123456"
})
am I approaching this wrong? I know that if i can get the idToken it will work. Do i rely need the browser for this? Thanks :)
From Exchange custom token for an ID and refresh token, you can transform a custom token to an id token with the api. Hence, you just have to generate a custom token first from the uid, then transform it in a custom token. Here is my sample:
const admin = require('firebase-admin');
const config = require('config');
const rp = require('request-promise');
module.exports.getIdToken = async uid => {
const customToken = await admin.auth().createCustomToken(uid)
const res = await rp({
url: `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=${config.get('firebase.apiKey')}`,
method: 'POST',
body: {
token: customToken,
returnSecureToken: true
},
json: true,
});
return res.idToken;
};
L. Meyer's Answer Worked for me.
But, the rp npm package is deprecated and is no longer used.
Here is the modified working code using axios.
const axios = require('axios').default;
const admin = require('firebase-admin');
const FIREBASE_API_KEY = 'YOUR_API_KEY_FROM_FIREBASE_CONSOLE';
const createIdTokenfromCustomToken = async uid => {
try {
const customToken = await admin.auth().createCustomToken(uid);
const res = await axios({
url: `https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken?key=${FIREBASE_API_KEY}`,
method: 'post',
data: {
token: customToken,
returnSecureToken: true
},
json: true,
});
return res.data.idToken;
} catch (e) {
console.log(e);
}
}
curl 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=<FIREBASE_KEY>' -H 'Content-Type: application/json'--data-binary '{"email": "test#test.com","password":"test","returnSecureToken":true}'
If this curl doesn't run, try running the same thing on Postman. It works!

Azure Mobile Services - Getting more user information

I inherited a Windows 8 application that is written with XAML. So in C# when I make this call
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
(This is for Azure Mobile Services)
The user object is ONLY giving me the Token and the MicrosoftAccount:..............
In order to get to authenticate people, I need to be able to see WHO is requesting access...
I looking at articles like below, but I seem to be missing something? Is this javascript in the article something I would have to write in Node.js?
Example article:
http://blogs.msdn.com/b/carlosfigueira/archive/2013/12/12/expanded-login-scopes-in-azure-mobile-services.aspx
Currently to be able to get more information about the logged in user, you need to make a second call to the service to retrieve the user info. You don't really need to ask for additional login scopes (the topic of the post you mentioned) to retrieve the user name, since that is given by default for all the providers.
This post should have the code you need to write in the server side (node.js) to get more information about the logged in user. The TL;DR version is given below:
On the server side: add this custom API (I'll call it "userInfo"; set the permission of GET to "user", and all others to admin):
exports.get = function(request, response) {
var user = request.user;
user.getIdentities({
success: function(identities) {
var accessToken = identities.microsoft.accessToken;
var url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + accessToken;
var requestCallback = function (err, resp, body) {
if (err || resp.statusCode !== 200) {
console.error('Error sending data to the provider: ', err);
response.send(statusCodes.INTERNAL_SERVER_ERROR, body);
} else {
try {
var userData = JSON.parse(body);
response.send(200, userData);
} catch (ex) {
console.error('Error parsing response from the provider API: ', ex);
response.send(statusCodes.INTERNAL_SERVER_ERROR, ex);
}
}
}
var req = require('request');
var reqOptions = {
uri: url,
headers: { Accept: "application/json" }
};
req(reqOptions, requestCallback);
}
});
}
On the client side, after a successful login, call that API:
user = await MobileServices.MobileService
.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
var userInfo = await MobileServices.MobileService.InvokeApiAsync(
"userInfo", HttpMethod.Get, null);
userInfo will contain a JObject with the user information. There is an open feature request to make this better at http://feedback.azure.com/forums/216254-mobile-services/suggestions/5211616-ability-to-intercept-the-login-response.

Categories