how send server data in a multi page web application? - javascript

These days I made a standard home page, a login page, and a user information page. In my login page using method POST and Ajax(Fetch), I send the e-mail and password to the server, the server responses the correct json with the user information that belongs to that e-mail. BUT I don't have an idea how that user information in JSON can use in another HTML document like a user information page.
I have tried so many things but nothing helped me.
form.addEventListener('submit',(e)=>{
e.preventDefault();
const data={}
data1.correo=email.value;
data1.contrasenia=password.value;
const miinit={
method:'POST',
headers: {"Content-type":"application/json"},
body:JSON.stringify(data1)}
const url='https://localhost:44351/api/login';
const myrequest =new Request(url,miinit)
const requestpost=()=>{
fetch(myrequest).then(response=>{console.log(response);return response.json()}).then(response=>{console.log(response)})
}
requestpost()
})

I'm assuming you are doing this all without any library or anything. With that being said, I'm assuming that each page transition is going to be a full page refresh. This means that your object returned by the API is only as good as the lifetime of your Login Page.
You have two options that will work with your setup:
Use localStorage to store the current users JSON payload.
const payload = { userId: 1, name: 'Test' };
// The item being saved to localStorage must be
// a string. You serialize/deserialize as your
// set/get if need be.
localStorage.setItem('currentUser', JSON.stringify(payload));
// On some other page, calling localStorage.getItem
// will give you the object
console.log(localStorage.getItem('currentUser'));
Assuming you have control of your backend code and there is a session available, create a new API Method that will return the current user object. You can call the getCurrentUser method on every page and that will return that logged in user.
Both of these options have security points to think about, but that isn't what this question is about. I think for you, the easiest option is to just save the object to Local Storage, but I would not recommend that for a production setup. If you are trying to do something that is production ready, I would recommend that you look into JWT Authentication.
https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
https://jwt.io/introduction/

If you want to use the information coming back from the server in more than one page, you'd have to store it in LocalStorage or another persistent API. Data is not shared across pages.

Related

Google oauth session lost after page reload (javascript)

I recently moved from the deprecated gapi.auth2 to the new Google Identity Services, using the javascript client library, and noticed a big difference: if someone signs in, and then reloads the page, the session is lost, and has to sign in again, every time the page is loaded. This was not the case with the deprecated library.
The problem can be easily reproduced with the Calendar API example.
Is there any configuration option to keep the session persistent? Or do I need to store the access tokens somehow? I could not find anything relevant in the official docs.
UPDATE:
The migration guide states the following:
Previously, Google Sign-In helped you to manage user signed-in status using:
Callback handlers for Monitoring the user's session state.
Listeners for events and changes to signed-in status for a user's Google Account.
You are responsible for managing sign-in state and user sessions to your web app.
However there's absolutely no information on what needs to be done.
UPDATE 2
To be more specific, the actual issue is not making the session persistent. Managing the sign in state and user session is something I can solve.
The real problem is the access token used to call the Google APIs.
As mentioned in the comments, the access tokens are 1) short lived 2) are not stored anywhere, so even if not expired, they do not persist between page reloads.
Google provides the requestAccessToken method for this, however even if I specify prompt: '', it opens the sign-in popup. If I also specify the hint option with the signed in user's email address, than the popup opens, displays a loading animation briefly, and closes without user interaction. I could live with this, however this only works if triggered by a user interaction, otherwise the browser blocks the popup window, meaning that I cannot renew the token without user interaction, e.g. on page load. Any tips to solve this?
I faced all the same issues you described in your question.
In order to help:
Google 3P Authorization JavaScript Library: in this link we can check all the methods the new library has (it does not refresh token, etc..)
This doc says the library won't control the cookies to keep the state anymore.
Solution
Firstly I need to thanks #Sam O'Riil answer.
As Sam described: "you can somehow save access token and use it to speed-up things after page reload."
Given the the Google's exampe, we should call initTokenClient in order to configure the Google Auth and the requestAccessToken to popup the auth:
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
prompt: 'consent',
callback: tokenCallback
});
tokenClient.requestAccessToken({prompt: ''})
In your tokenCallback you can save the credentials you get somehow, e.g.:
const tokenCallback(credentials) => {
// save here the credentials using localStorage or cookies or whatever you want to.
}
Finally, when you restart/reload your application and you initialize the gapi.server again, you only need to get the credentials again and set token to gapi, like:
gapi.load('client', function() {
gapi.client.init({}).then(function() {
let credentials = // get your credentials from where you saved it
credentials = JSON.parse(credentials); // parse it if you got it as string
gapi.client.setToken(credentials);
... continue you app ...
}).catch(function(err) {
// do catch...
});
});
Doing it, your application will work after the reload. I know it could not be the best solution, but seeing what you have and the library offers, I think that's you can do.
p.s.: the token expires after 1 hour and there is no refresh token (using the implicit flow) so, you will have to ask the user to sign-in again.

Sending cookies between Flask and javascript and using Flask-session

I am a web-dev noob but I'll try my best to be clear about the problem, explain my approach, and what I've tried. I also include imports in-case those are causing the problem but I'm pretty sure I've isolated it to what I describe.
I am trying to use Flask-session to keep information private but some values are "lost". This code is trimmed down significantly for simplicity. The user lands on /send and we render the loading template. loading.js does fetch() on /deploy while it runs an animation, then() we go to /results when the deploy function is done.
loading.js
function navigate() {
window.location.href = 'results'; // redirect to results page when done!
}
// deploy the contract while the loading screen goes then navigate to results page
const data = fetch('deploy').then(navigate);
loopThroughMessages(messages);
main.py
from flask_session import Session
app = Flask(__name__,
static_folder='static',
template_folder='templates')
# for the session, i.e passing values
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config.from_object(__name__)
Session(app)
#app.route('/send')
def main():
# take url parameters and do stuff with them
return render_template('loading.html')
#app.route("/deploy")
def deploy_contract():
session['contract_address'] = some_fnc()
# fetch() requires that this function return a json
return {}
#app.route("/results")
def serve_results_page():
# pull saved values from the session
data = {'contract_key' : session['contract_address']
} # calling session here causes the error, the contract_address key doesn't exist
return render_template('results.html', data=data)
So contract_address is saved to the session but when we get to /results, the server has no way to associate that session with the client.
We want to keep our contract_address private so sending it to loading.js is not an option. I'm guessing that since http is stateless, I need to pass a cookie to and from my js and python files but I'm a bit lost on how to implement it. Are cookies unnecessary (because the server doesn't actually need to receive any data from my js files)? Should I be using redirects or something besides fetch()?
Hacky fixes, different approaches, and resources are all welcome. I feel like I'm close, like there's a simple way to use cookies that I'm overlooking.
I will be continuing to research and detail the approaches I'm considering
Edit1: Looking at Flask's should_set_cookie method
Try fetch with credentials:'include' to cause browsers to send a request with credentials included on the server side calls:
fetch('deploy', {
method: 'GET',
credentials: 'include'
}).then(navigate);
Using this, you will access session['contract_address'] in the results route.
The flask-session sets a cookie with a key session in your browser, fetch with credentials:'include' includes this cookie value in the network call.
Session in flask is implemented as a client session, saving all session content as client cookies. The flask-session extension provides some other server storage for session. E.g. app.config["SESSION_TYPE"] = "filesystem" save session content in a file on the server.
But both of the approaches still depends on Cookie. The server-side session storage need to get a session_id from client Cookie.
You need to enable cookie sending on Fetch API.
fetch, sending cookies

When to fetch data from API via Vuex again to keep the data up to date

I'm fetching data from an API and want to achieve it via Vuex. So my users store module is holding and managing all users.
To fetch all users I have a fetch action which calls the API via Axios and passes the users array from the response to the mutation.
I have a view component at /users which renders a list of users. Within that component I call
async mounted() {
await this.fetch();
}
to initialize the store. After that, the store getter will return the users array and the list gets rendered. I know that I can also fetch one item by id via getter
getById: state => id => state.users.find(user => user.id === id)
When navigating to a user detail page /users/:id I can make use of it, extract the userId via this.$router.currentRoute.params.id and render the user details. But what if I load that route directly? Then the store initialized with an empty users array and it won't find a single user.
So should I fetch all users again in the mounted hook of that user details view? I think calling the axios service directly would be an anti pattern because the resources say I should use Vuex as an interface between the component and the API.
Is there a best practise when to fetch all users from the api? Because let's image you navigate to another page which loads all users in a select component, should I fetch them from the getter? Maybe it will load a user that doesn't exist anymore. But polling all users every X seconds would be bad too...
What is the correct way to update the store data to make it accessible for all components in the entire application?
I don't agree that Vuex should be used to store an entire tables worth of user data in the manner you described. This sounds like a security concern to me. Remember that Vuex is a client-side store; meaning all your user data would be visible to any client using your application. You should never store sensitive data client-side.
Type this into your browser console, and you'll see what I mean.
document.getElementsByTagName('a')[0].__vue__.$store.state
Vuex should be used for storing non-sensitive data of an individual user required to drive their session.
If you are storing all user data for the purpose of listing accounts so that they can be managed by an admin or something, then I would instead, make that API call on demand when you load whatever view you have which lists your user accounts. This way you will always have the most up-to-date data, without the need for constantly polling your API.
I would paginate that data, loading only the first 5 records (to keep the API call initially light), then provide your admins the ability to search for accounts; that searching should be done server side, through your API.
Example:
methods: {
fetchUsers(limit, page, search) {
const thisIns = this;
if (search) {
this.userQuery = `/users/find?search=${search}`
} else {
this.userQuery = `/users/find?limit=${limit}&page=${page}`
}
thisIns.$http.get(this.userQuery)
.then(async (response) => {
console.log(response.data)
})
.catch(function (error) {
console.log(error)
});
}
},
created() {
this.fetchUsers(5, 1)
},
You can then call your fetchUsers() method as needed, such as when performing a search for user(s).
Since you are using Axios, you can also consider implementing a caching mechanism, axios-cache-adapter, so that your API calls are cached for some period of time you define. This would reduce the number of API calls hitting your server.
I also don't consider this to be scaleable. Remember that an application user will have to wait for this payload when they first load your app. This also has performance implications; as your user data grows over time, you'll be consuming more and more of the devices RAM.

Unable to retrieve current Parse user

I'm logging into my Parse app through the JavaScript SDK, it appears to be storing cookies however once it progresses to the next page it always displays the current user as being null despite having logged in successfully. I've cleared the cookies and it appears to be storing the cookies after login fine. This is the code I'm using however no matter what I seem to do it just won't collect the current user. Does anyone know if there's an issue with this or if there's something extra I have to do for it to be able to recall the cookie? If it's relevant the two site are on subdomains, could this be the problem?
Parse.initialize(JSDK, API);
var currentUser = Parse.User.current();
var currentUsername = currentUser.get('username');
alert(currentUsername);
You should use the getUsername() method instead of get('username').

User profile info getting reset after each page refresh (using Hello.js)

I'm using Hello.js in my AngularJS application to let users authenticate with Facebook. Once the user is logged in and I get the user information from Facebook, I use the json and get the user object from my own database and store it in the root scope, so that various areas in my site can access the user's profile info (for example in the header where I display the logged in user's name).
Here's the service that handles the authentication stuff
angular.module('myApp')
.factory('userService', function($rootScope, $location) {
// Hello.js Functions
hello.init({
facebook : '1234567891234545'
});
var service = {
isLoggedIn: function() {
return $rootScope.loggedInUser != null;
},
login: function() {
hello('facebook').login( function() {
hello('facebook').api('/me').success(function(json) {
$rootScope.loggedInUser = getUserFromMyOwnAPI(json.id);
$rootScope.$apply(function() {
$location.path('/');
});
});
});
},
logout: function() {
hello('facebook').logout( function() {
$rootScope.loggedInUser = null;
$location.path('/');
});
},
loggedInUser: function(){
return $rootScope.loggedInUser;
}
}
return service;
})
The issue I'm having is that every time I refresh the page, I lose the profile info. Makes sense because $rootScope.loggedInUser which stores the user data (based on the json I got back from Facebook) would get reset after a page refresh.
How should I handle this? Should I be putting the user data in localStorage instead of the rootScope? Or should I somehow be leveraging hello('facebook').api('/me') each time I want to reference the user's profile info?
I noticed that hello.js already stores something in localStorage:
key: hello
{"facebook":{"state":"","access_token":"blahblahblah","expires_in":6776,"https":"1","client_id":"12345","network":"facebook","display":"none","redirect_uri":"http://adodson.com/hello.js/redirect.html","scope":"basic","expires":1412632794.806}}
So I 'm wondering if I would be duplicating the effort by adding the user object to localStorage.
The answer to this question is subjective and could be argued different ways. However, based on your question and comment, my opinion is that retaining (non-sensitive) user profile information in local storage would be a good option to provide an uninterrupted user experience.
Never store the user information including but not limited social network info inside the localStorage, cookie, and/or other unsecured mechanisms. Even if you need to make a compromise on a not so critical information, then use memory (like scope variables). You also have to account for complexities arising on storing user info in localStorage such as when user logs out of the social network. Now, session tracking is as old of WWW itself. How to track the sessions? There are countless articles discussing the pro and cons of Cookies, Json Web Tokens, Session State in server side, etc. My personal opinion is to store the most of the user info in server side and link the current user to that session using a session ID stored in any possible medium like Cookie, Query Param, localStorage etc. Thats only useful if you have a backend and your OAuth provides you a token. I dont see anywhere in hello.js that it provides you a token. So given that you shouldnt store any client side user info in browser, and hello.js doesnt provide you with a token to reuse in subsequent calls my advice to you is to login the user every single time.
about your code:
As Adin and DanArl already stated, you can implement the process of the user's session tracking in many different ways - preferably server side, identified via some kind of identifier stored in a session cookie.
Concerning your actual code, you may have a look at jshint:
Three warnings
10 Use '!==' to compare with 'null'.
31 Missing semicolon.
34 Missing semicolon.
Three undefined variables
1 angular
4 hello
13 hello
14 hello
23 hello
15 getUserFromMyOwnAPI
You should inject 'hello' correctly, by passing 'hello' to your 'userService'.
(have a look at: https://docs.angularjs.org/guide/di if you want more information about dependency injection in AngularJS)
about your actual problem:
After you have received the token - from your prefered way of storing it - you should be able to inspect and validate the token via:
GET graph.facebook.com/debug_token?
input_token={token-to-inspect}
&access_token={app-token-or-admin-token}
Source: https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow/v2.1#checktoken
Then you may have a shot at passing this information to helljs (i'm used to this library)

Categories

Resources