How to safely store a unique client id for websockets? - javascript

I need to be able to know which client is sending a request to the server through web-sockets and tell it apart from other clients. The solution is a client id but I don't know the best way to store it so that the client can send it along with every request.
client
var clientId = null;
if (response.method === "connect"){
clientId = response.clientId
}

You could wrap your code with an IIFE which remove the possibility to access variables in the global scope outside of the IIFE. So only the code that runs inside of this IIFE can access the clientId variable. But changing the clientId from, for example, the console wouldn't change a thing.
(function() {
// Put all of your code in this scope.
var clientId = null;
if (response.method === "connect"){
clientId = response.clientId
}
})();
If only overwriting the variable is a problem, then you could alternatively use the Web Storage API to save the id in the browser, either permanently or until the tab has been closed. So you could store it like in the example below.
if (response.method === "connect"){
sessionStorage.setItem('clientId', JSON.stringify(response.clientId));
}
And use clientId further down the script, or even in another script on the same page with the following lines:
var storedClientId = sessionStorage.getItem('clientId');
if (storedClientId !== null) { // Check if the id has been set.
var clientId = JSON.parse(storedClientId);
}
So the IIFE will make modification to your variables impossible outside of its own scope. And the Web Storage API will store your client id in the browser and not in a variable.

Related

Using Promise for a function that is not called by JavaScript

I am using Google Sign-In and have the following problem:
sessionStorage.getItem('userEntity') returns null when I close the browser and open it again. It does not return null if I just reload the page.
The session will be set in this function:
// Signing in
function onSignIn(googleUser) {
var profile = googleUser.getBasicProfile();
// Store the entity object in sessionStorage where it will be accessible from all pages
let userEntity = {};
userEntity.id = profile.getId();
userEntity.name = profile.getName();
userEntity.img = profile.getImageUrl();
userEntity.email = profile.getImageUrl();
sessionStorage.setItem('userEntity',JSON.stringify(userEntity));
userIsLoggedOut.style.display = 'none';
userIsLoggedIn.style.display = 'flex';
document.querySelector('.user-img').setAttribute('src', userEntity.img);
}
I do not run this function in JavaScript. The following HTML will make it run:
<div class="g-signin2" data-onsuccess="onSignIn"></div>
Even if I put console.log(sessionStorage.getItem('userEntity')) after the function or on the bottom of my JavaScript, it still returns null.
I guess a solution would be using Promise but how can I use it if I don't run the function onSignIn() in JavaScript?
The data stored in SessionStorage are deleted when you close the tab or the browser, more details below:
https://www.w3schools.com/jsref/prop_win_sessionstorage.asp
You can use localStorage instead to keep the data in the browser even if you close the tab or the browser, so change the following line:
sessionStorage.setItem('userEntity',JSON.stringify(userEntity));
by
localStorage.setItem('userEntity',JSON.stringify(userEntity));
But you have to take care about the security of the informations stored in the localStorage, you can deal with secure cookies if you need to store secure data client side.
You can copy item from sessionStorage to localStorage:
localStorage.setItem('userEntity', sessionStorage.getItem('userEntity'));
It will be persistent, so you can use it anytime:
localStorage.getItem('userEntity');

Save data from GET request as variable

I've got a simple axios GET request that is working with Azure directline 3.0. The GET request pulls back data and shows it in the console (as seen in the picture).
The data I want to save into a variable is the conversationId. I then want to use this variable with Axios Post in another JS file to post as part of the link e.g. let URL = "https://directline.botframework.com/v3/directline/conversations/"+convID"/activities". With convID being the variable I wish to create. Right now I am manually changing the convID with the new conversation ID, but I want to create a variable so I can place it in the post javascript file so it is automatic.enter image description here
There are various ways you can solve this problem. Easiest one being, exposing the data on a shared object window.convID = convID and then accessing it wherever required.
You could also look to make singleton objects, which can be instantiated only once and hence will share it's variables across the lifespan of the application.
axios.get(/* URL */).then(res => {
window.convID = res.data.conversationId;
});
You can save that variable value in LocalStorage. Something like this:
let routeToYourApi = '/api/conversations'; // or something like this...
axios.get(routeToYourApi).then((response) => {
let conversationId = response.data.conversationId; // not sure if data object from your image is directly nested inside response that you get from server, but you get the idea...
window.localStorage.setItem("convId", conversationId);
}) // here you can fetch your conversation id
Than you can access it anywhere in your app:
let conversationId = window.localStorage.getItem("convId");
... and eventually remove it from local storage:
window.localStorage.removeItem("convId")
Hope this will help you!

Why does setting session variable in callback of geolocation.getCurrentPosition not work

In my meteor app I am trying to set user location first thing in
Meteor.startup(() => {...}
like this:
navigator.geolocation.getCurrentPosition((succ) => Session.set('currentLocation',succ));
However, when trying to access this later in the app it returns an empty object.
I validated setting a static session variable like 'hi', which works fine. I also validated that the callback gets called with console.log(succ), which also works fine. My best guess is some overwrite of the session variable is happening, I can't figure out how to test for that though.
Any ideas?
Please Check This Article Meteor Session
And Location Fetch Code is
navigator.geolocation.getCurrentPosition(success, error);
function success(position) {
Session.set("Latitude",position.coords.latitude);
Session.set("Longitude",position.coords.longitude);
}
function error() {
console.log("unable to retrive your location");
}
And Retrive session data
let Latitude = Session.get("Latitude");
let Longitude = Session.get("Longitude");
Note:Session Work Only Client Side.

cross domain localstorage with javascript

We have a javascript api.js which is hosted on domain api.abc.com. It manages the local storage.
We included this javascript in our websites at abc.com and login.abc.com as a cross domain js like
<script src="http://api.abc.com/api.js">
I understand that localstoarge is per domain basis. However since api.js is loaded from api.abc.com, I expected that it will have access to local storage of api.abc.com from both the domains. Unfortunately, it doesn't seem to be the case. When api.js stores a value in localstoarge from one domain, it's not accessible to it when loaded from other domain.
Any idea?
How about using cross domain postmessage and iframes?
So on your wrong-domain-page you include an iframe that posts messages with the cookie data back.
Here is a solid example of cross domain postmessages:
http://blog.teamtreehouse.com/cross-domain-messaging-with-postmessage
live example:
http://codepen.io/anon/pen/EVBGyz //forked sender code with a tiiiiiny change :) :
window.onload = function() {
// Get the window displayed in the iframe.
var receiver = document.getElementById('receiver').contentWindow;
// Get a reference to the 'Send Message' button.
var btn = document.getElementById('send');
// A function to handle sending messages.
function sendMessage(e) {
// Prevent any default browser behaviour.
e.preventDefault();
// Send a message with the text 'Hello Treehouse!' to the new window.
receiver.postMessage('cookie data!', 'http://wrong-domain.com');
}
// Add an event listener that will execute the sendMessage() function
// when the send button is clicked.
btn.addEventListener('click', sendMessage);
}
Receiver code:
window.onload=function(){
var messageEle=document.getElementById('message');
function receiveMessage(e){
if(e.origin!=="http://correct-domain.com")
return;
messageEle.innerHTML="Message Received: "+e.data;
}
window.addEventListener('message',receiveMessage);
}
As noticed in your post the localStorage (sessionStorage too) won't be stored on the storage related to the domain api.abc.com. If this was the case, by using CDN version of a library using localStorage you would have to share storage with all the other websites using this library.
One good solution could be to use an iframe with postMessage as explained in the following stack overflow:
use localStorage across subdomains
You might try this cross-storage from Zendesk. Basically,
There are hubs and clients:
hubs: reside on any server, interact directly with LocalStorage API
clients: load the hub using an embedded iframe, and post messages, interact with data
Key things is you can configure the permission (get, set, delete) that each host or domain client could have.
The library is divided into two types of components: hubs and clients.
Care should be made to limit the origins of the bidirectional
communication. As such, when initializing the hub, an array of
permissions objects is passed. Any messages from clients whose origin
does not match the pattern are ignored, as well as those not within
the allowed set of methods. The set of permissions are enforced thanks
to the same-origin policy. However, keep in mind that any user has
full control of their local storage data - it's still client data.
This only restricts access on a per-domain or web app level.
The other answers all ignore the fact that you're not really operating cross-domain, just between subdomains.
You still need a hidden iframe to encapsulate the origin of the localStorage store you want to access (api.abc.com), but by setting document.domain = "abc.com" on both main window and hidden iframe, they can interact directly. (Though note that this is deprecated, may have security implications, and in Chrome at least requires also sending a Origin-Agent-Cluster: ?0 header).
Then you can literally just use hiddenIFrame.contentWindow.localStorage instead of window.localStorage, and forget about the headache of doing anything via postMessage().
I posted a more detailed version of this answer here: https://stackoverflow.com/a/63602446/999120
Use iframe to store data in local storage & postMessage API to communicate between parent domain & iframe
Create Iframe with message event listener to store data in local storage of iframe domain
window.addEventListener("message", handleMessage, false);
function handleMessage(e) {
let {key, value, method} = e.data;
if (method == 'store') {
window.localStorage.setItem(key, value); // Store data in iframe domain local storage
} else if (method == 'retrieve') {
let response = window.localStorage.getItem(key);
e.source.postMessage({
key,
response,
method: 'response'
}, '*'); // Retrieve local storage data
}
}
Pass Message from parent domain to iframe to store data
document.getElementById('myFrameId').contentWindow.postMessage({
key: 'key',
value: data,
method: 'store'
});
Retrieve data from Iframe
document.getElementById('myFrameId').contentWindow.postMessage({
method: 'response',
key: 'key'
});
window.addEventListener("message", handleResponse, false);
function handleResponse(e) {
let {key,value,method} = e.data
if (method == 'response') {
console.log('Response Key', key);
console.log('Response value', value)
}
}

Javascript Web App Best Practices

I'm having a hard time writing my question succinctly and clearly, so let me describe what I'm working with.
I'm building a web application that:
has it's own API hosted on a subdomain (https://api.example.com)
has the main application hosted on the tld (https://www.example.com)
the tld doesn't have any database access, but instead interacts with the API to work with data
the tld authenticates with the api through OAuth and stores the access token and access token secret in a session
when the session ends, the access token is no longer used, thus logging the user out
I have a route in the tld (let's call it /ajax for this question) that the javascript calls (GET, PUT, POST, OR DELETE) to make requests to the api. This way, nobody ever has to see the access token, access token secret, consumer key, or consumer secret.
The way I see it, the access token and access token secret are really the only things I need to store in a session since I can grab everything else using the API, but instead of making a call every single time for every piece of data I need, I think some things should persist, like aspects of the user's profile, layout preferences, etc.
What is the best way for me to accomplish this? Local storage? Cookies? Should I scrap this and just store it in sessions?
And if you have some time, what other best practices are there for building sites like this that I may not know of?
You are on the right track I would say, but store your data in JavaScript primarily. And couple it with Local Storage when suitable.
When I build apps such as the one you are describing I usually take care to set up JavaScript representations of the data I receive via the API.
One such representation could look as follows below. Bear in mind that my example code below makes a couple of assumptions.
It makes the assumption that you have an api object defined which takes care of API calls, and invokes a callback on completion.
that the data returned by the API is JSON that simply can be assigned to a JavaScript variable,
That the JSON returned is a list of objects, each with an "id" field.
That you have some sort of event object, I usually build my own custom events that basically carry function objects as listeners and when fired go through the listeners and invoke the functions with or without a payload depending on the situation.
Data container example:
MYAPP.data.BaseContainer = function (api_url, loadedEvent) {
var self = {
// Array to store the data returned via the APIs
_data : [],
// The API URL used to fetch data
api_url : api_url,
// Boolean flag to signify whether the _data variable has been populated
is_loaded : false,
// The even to fire once _data has been populated
loadedEvent : loadedEvent,
/**
* Returns the state of the is_loaded variable
*/
loaded : function () {
return self.is_loaded;
},
/**
* Takes an ID and returns any member of the _data array
* that has that ID.
*
* #param id : an String or integer representing the ID.
* #returns {Object}
*/
byId : function (id) {
var toReturn = null;
for (var i = 0, len = self._data.length; i < len; i++) {
if (self._data[i].id == id) {
toReturn = self._data[i];
break;
}
}
return toReturn;
},
/**
* Returns the entire _data array.
*/
all : function () {
return self._data;
},
/**
* This simple callback just stores the json response in
* its entirety on the _data variable.
*/
callback : function(data) {
self._data = data;
self.is_loaded = true;
loadedEvent.fire(self._data);
},
/**
* Calls the API, if no callback has been specified as a parameter
* self.callback is used.
*/
getFromAPI : function(callback) {
if (typeof callback === 'undefined') {
callback = self.callback;
}
api.get(self.api_url, callback);
}
};
self.getFromAPI();
return self;
};
With this blueprint I can now create specific data containers like this:
/**
* Stores a list of "friends" gotten from the API.
* This is basically an instance of the BaseContainer object defined above.
*/
MYAPP.data.Friends = (function () {
var self = MYAPP.data.BaseContainer("API_URL_TO_FECTH_FRIENDS_LIST", FriendsLoadedEvent);
return {
byId : self.byId,
all : self.all,
loaded : self.loaded
};
}());
As soon as this code is run, an API call is made, and the FriendsLoadedEvent will be fired when it is done. So, to put it bluntly, I use JavaScript to store my stuff usually. But if you want to throw LocalStorage into the mix that is easy too!
Just add local storage code to the BaseContainer object that first detects whether the client actually supports localstorage, and if so mirror the _data field in local storage. This is handy to keep often used data quickly available between sessions. Use the readily available JSON parsing tools to convert the data from JSON to LocalStorage "text"and back.
Just keep in mind that you cannot rely on LocalStorage as your primary data structure, you have no guarantee that the client supports it, and even when it does the upper bounds for how much data you can actually store is different between the browsers. So use it to store data that:
You want access to very often,
that you feel should just be there, immediately as soon as the user logs in,
and that does not change often enough to warrant refreshing API calls constantly.
Congratulation! You've answered most of your question already. If you want to persist user data, you'll need to use something like local storage or cookies. In your case local storage is best. With cookies, each page request sends to cookies along in the header.
Best of Luck with your app.

Categories