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

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)

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.

Single flow: sign user in via Google oAuth AND grant offline/server access?

I'm trying to implement Google sign-in and API access for a web app with a Node.js back end. Google's docs provide two options using a combo of platform.js client-side and google-auth-library server-side:
Google Sign-In with back-end auth, via which users can log into my app using their Google account. (auth2.signIn() on the client and verifyIdToken() on the server.)
Google Sign-in for server-side apps, via which I can authorize the server to connect to Google directly on behalf of my users. (auth2.grantOfflineAccess() on the client, which returns a code I can pass to getToken() on the server.)
I need both: I want to authenticate users via Google sign-in; and, I want to set up server auth so it can also work on behalf of the user.
I can't figure out how to do this with a single authentication flow. The closest I can get is to do the two in sequence: authenticate the user first with signIn(), and then (as needed), do a second pass via grantOfflineAccess(). This is problematic:
The user now has to go through two authentications back to back, which is awkward and makes it look like there's something broken with my app.
In order to avoid running afoul of popup blockers, I can't give them those two flows on top of each other; I have to do the first authentication, then supply a button to start the second authentication. This is super-awkward because now I have to explain why the first one wasn't enough.
Ideally there's some variant of signIn() that adds the offline access into the initial authentication flow and returns the code along with the usual tokens, but I'm not seeing anything. Help?
(Edit: Some advice I received elsewhere is to implement only flow #2, then use a secure cookie store some sort of user identifier that I check against the user account with each request. I can see that this would work functionally, but it basically means I'm rolling my own login system, which would seem to increase the chance I introduce bugs in a critical system.)
To add an API to an existing Google Sign-In integration the best option is to implement incremental authorization. For this, you need to use both google-auth-library and googleapis, so that users can have this workflow:
Authenticate with Google Sign-In.
Authorize your application to use their information to integrate it with a Google API. For instance, Google Calendar. 
For this, your client-side JavaScript for authentication might require some changes to request
offline access:
$('#signinButton').click(function() {
auth2.grantOfflineAccess().then(signInCallback);
});
In the response, you will have a JSON object with an authorization code:
{"code":"4/yU4cQZTMnnMtetyFcIWNItG32eKxxxgXXX-Z4yyJJJo.4qHskT-UtugceFc0ZRONyF4z7U4UmAI"}
After this, you can use the one-time code to exchange it for an access token and refresh token.
Here are some workflow details:
The code is your one-time code that your server can exchange for its own access token and refresh token. You can only obtain a refresh token after the user has been presented an authorization dialog requesting offline access. If you've specified the select-account prompt in the OfflineAccessOptions [...], you must store the refresh token that you retrieve for later use because subsequent exchanges will return null for the refresh token
Therefore, you should use google-auth-library to complete this workflow in the back-end. For this,
you'll use the authentication code to get a refresh token. However, as this is an offline workflow,
you also need to verify the integrity of the provided code as the documentation explains:
If you use Google Sign-In with an app or site that communicates with a backend server, you might need to identify the currently signed-in user on the server. To do so securely, after a user successfully signs in, send the user's ID token to your server using HTTPS. Then, on the server, verify the integrity of the ID token and use the user information contained in the token
The final function to get the refresh token that you should persist in your database might look like
this:
const { OAuth2Client } = require('google-auth-library');
/**
* Create a new OAuth2Client, and go through the OAuth2 content
* workflow. Return the refresh token.
*/
function getRefreshToken(code, scope) {
return new Promise((resolve, reject) => {
// Create an oAuth client to authorize the API call. Secrets should be
// downloaded from the Google Developers Console.
const oAuth2Client = new OAuth2Client(
YOUR_CLIENT_ID,
YOUR_CLIENT_SECRET,
YOUR_REDIRECT_URL
);
// Generate the url that will be used for the consent dialog.
await oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope,
});
// Verify the integrity of the idToken through the authentication
// code and use the user information contained in the token
const { tokens } = await client.getToken(code);
const ticket = await client.verifyIdToken({
idToken: tokens.id_token!,
audience: keys.web.client_secret,
});
idInfo = ticket.getPayload();
return tokens.refresh_token;
})
}
At this point, we've refactored the authentication workflow to support Google APIs. However, you haven't asked the user to authorize it yet. Since you also need to grant offline access, you should request additional permissions through your client-side application. Keep in mind that you already need an active session.
const googleOauth = gapi.auth2.getAuthInstance();
const newScope = "https://www.googleapis.com/auth/calendar"
googleOauth = auth2.currentUser.get();
googleOauth.grantOfflineAccess({ scope: newScope }).then(
function(success){
console.log(JSON.stringify({ message: "success", value: success }));
},
function(fail){
alert(JSON.stringify({message: "fail", value: fail}));
});
You're done with the front-end changes and you're only missing one step. To create a Google API's client in the back-end with the googleapis library, you need to use the refresh token from the previous step.
For a complete workflow with a Node.js back-end, you might find my gist helpful.
While authentication (sign in), you need to add "offline" access type (by default online) , so you will get a refresh token which you can use to get access token later without further user consent/authentication. You don't need to grant offline later, but only during signing in by adding the offline access_type. I don't know about platform.js but used "passport" npm module . I have also used "googleapis" npm module/library, this is official by Google.
https://developers.google.com/identity/protocols/oauth2/web-server
https://github.com/googleapis/google-api-nodejs-client
Check this:
https://github.com/googleapis/google-api-nodejs-client#generating-an-authentication-url
EDIT: You have a server side & you need to work on behalf of the user. You also want to use Google for signing in. You just need #2 Google Sign-in for server-side apps , why are you considering both #1 & #2 options.
I can think of #2 as the proper way based on your requirements. If you just want to signin, use basic scope such as email & profile (openid connect) to identify the user. And if you want user delegated permission (such as you want to automatically create an event in users calendar), just add the offline access_type during sign in. You can use only signing in for registered users & offline_access for new users.
Above is a single authentication flow.

how send server data in a multi page web application?

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.

How to process tracked information in Application Insights

I am using Application Insights to track events in my web pages:
appInsights.trackEvent("my-event", { test: true });
However I can see that each entry in the log, collects some info regarding several other things like:
User Id
Session Id
Operation name
The last one is sensitive as I can get the name of the computer or some other stuff. In order to comply to the GDPR, I wanna strip out those info from my log.
How do I tell Application Insights, to process the data before logging them? In my case, I would like to get access to the object which will be sent out by trackEvent and modify it before it is transmitted.
You can use TelemetryInitializers for that. They allow you to modify items before they are send to Application Insights
In your case it could be as simple as
appInsights.queue.push(function () {
appInsights.context.addTelemetryInitializer(function (envelope) {
envelope.tags['ai.operation.name'] = 'xxx';
});

How do I do anonymous Firebase logins which allow browser refresh?

I'm doing a javascript single-page app which allows people to log in, either via twitter or (for some use cases) anonymously.
A very important thing to figure out was how to let them reload the page -- this shouldn't force them to log back in!
I figured this out pretty quickly for the twitter login, and so it uses cookie-stored information to log back in (specifically, the user_id, oauth_token and oauth_token_secret).
However, I can't seem to make this work with the anonymous login facility.
I tried:
auth.login("anonymous", {
user_id: #get("userId"),
firebase_auth_token: #get("firebaseAuthToken")
});
but it doesn't work... I get a new anonymous user ID. I want to keep the same one for the duration of the user's browser session.
And yeah, I tried both user_id and id, firebaseAuthToken and firebase_auth_token.
Thanks!
By default, sessions are created any time you successfully log in a user, and last up until the session expiration time configured under the 'Auth' tab in Forge. This built-in sessioning applies to all Simple Login authentication types, and is automatic as long as local storage and cookies are available.
To resume a session, simply instantiate the FirebaseSimpleLogin object with a Firebase reference and callback. If a local session exists, the callback will be invoked with the same payload you would see if you had just logged the user in for the first time. Invoking the login method will always generate a brand new auth. flow regardless of current user authentication state or session.
Note that in anonymous auth, once a user session expires it cannot be recovered. This may change in the future or some additional functionality may be added to enable it, but it is currently only logged-in to once per user id.

Categories

Resources