Firebase Auth Web: Logout with page reload - node.js

I use Firebase with VueJS. Sign-up, sign-in works fine so far but as soon as I reload the page with F5 or load a page with writing to the browsers address bar directly then the auth session is killed, the user isn't signed-in anymore. It works fine as long as I don't reload the page, even if I click route links or get redirected to other pages, the user session keeps alive. It doesn't matter where I reload the page btw.
My login method:
firebase.auth().signInWithEmailAndPassword(this.username, this.password).then(
(user) => {
if (user.emailVerified === false) {
firebase.auth().signOut()
} else {
// redirect to lobby page if user is verified and signed in
this.$router.push('/lobby')
}
},
function(err) {
console.log(err.message)
}
)

You need to call firebase.auth().onAuthStateChanged in order to detect whether a user is logged in.
This method gets invoked in the UI thread on changes in the authentication state:
Right after the listener has been registered
When a user is signed in
When the current user is signed out
When the current user changes
Source
Example usage can be like this:
firebase.auth().onAuthStateChanged(user => {
if (user) {
if (user.emailVerified === false) {
firebase.auth().signOut()
} else {
this.$router.push('/lobby')
}
} else {
// will get to here from a logout event
// this.$router.push('/login')
}
}

Use this in your main.js file:
firebase.auth().onAuthStateChanged(() => {
if (!app) {
app = createApp(App).use(store).use(router).mount('#app')
}})
Firebase will run before Vue app is created.

Related

MSAL 2.0 Redirect User to Login Screen Fast

I use below libraries:
"#azure/msal-browser": "^2.16.0",
"#azure/msal-react": "^1.0.1",
I need to redirect the user to login screen without user being able to see any content. How would I be able to do that?
Currently, I have this:
export default function Main() {
const msalInstance = new PublicClientApplication(msalConfig);
useEffect(() => {
msalInstance
.handleRedirectPromise()
.then((s) => {
if (s === null) msalInstance.loginRedirect(loginRequest); // not logged in
else console.log(s); // logged in
})
.catch((a: any) => {
console.log("err");
console.log(a);
});
}, []);
return <App instance={msalInstance} />;
}
But, it loads the app for a second then does the redirection.
In order to achieve that specific behavior, there are two options:
Delay rendering your app until the user is confirmed to be logged in (they will be redirected to authenticate before you render your app).
Handle this on the server side by redirecting to the login interaction before the app is rendered (which would involve a solution other than msal-react).
Here is some documentation on the MSAL AuthenticationTemplate, which may be of some help in determining what the right way to use MSAL React would be in this case.
As a final note, it's not recommended that you initialize the PublicClientApplication inside a React component. Please review the Getting Started documentation and the react-router-sample for more guidance on how to initialize MSAL in your application.

load last viewed page on chrome extension startup

I'm developing my first chrome extension and I'm stuck with a "session restore" problem. At the begging of the developement when a user log in to my extension and close the popup, when he open the plugin again he had to login again.
I've used chrome.storage.sync to be able to save the session infomation to make the user to be still logged in and if he close the plugin, and his session is still active he will be redirected to the welcome page. But how can I check at the extension's startup in what page of the plugin the user was and bring him back to that page?
For example, if a user is logged and was on the "choose a book" section, how can i make the plugin open at "choose a book" section and not in "welcome" section?
Angular 2 for client side
NodeJs for server side
Consider that the session object is something like:
{
logged: true,
last_section: 'books'
}
When the user visits the books section, save it.
// this code goes inside some listener for visiting a section
chrome.storage.sync.get('session', function (items) {
const session = items.session || {}
session.last_section = 'books'
chrome.storage.sync.set({ session })
})
At the beginning of the popup script, you can simply call chrome.storage.sync.get to get the last session object state.
chrome.storage.sync.get('session', function (items) {
const session = items.session
if (session && session.logged) {
if (session.last_section === 'books') {
// render books section
}
if (session.last_section === 'welcome') {
// render welcome section
}
}
})

Vue.js authentication after manually entering URL

In my routes array in main.js, I have set
meta : { requiresAuth: true }
for every component except on the UserLogin page.
Regarding navigation resolution, I have set a check before each route as follows:
router.beforeEach((to, from, next) => {
const currentUser = firebase.auth().currentUser;
const requiresAuth = to.matched.some(record => record.meta.requiresAuth);
const currentUserState = !!currentUser;
console.table({ currentUserState, requiresAuth });
if (requiresAuth && !currentUser) {
next('/login');
} else {
next();
}
}
With this setup, everything almost works as expected/intended. Logged in users can go anywhere, and anonymous users are redirected to localhost/login.
However, this checking and redirecting only functions when users are navigating by clicking the links in the web app (which are all <router-link>s that go :to="{ name: 'SomeComponent' }".
When any user (registered or anonymous) attempts to navigate their way around the app by typing or copy pasting urls, the app will automatically send them to the login page.
From the console.table output, I can see that manually entering urls always results in currentUserState being false.
It does not remain false: a user that has logged in (currentUserState === true), who then manually navigates somewhere (currentUserState === false), has their currentUserState revert back to true
I can only vaguely guess that the problem is related to rendering and firebase.auth().
firebase.initializeApp(config)
// you need to let firebase try and get the auth state before instantiating the
// the Vue component, otherwise the router will be created and you haven't
// resolved whether or not you have a user
firebase.auth().onAuthStateChanged(function(user) {
app = new Vue({
el: '#app',
template: '<App/>',
components: { App },
router
})
});
I have found a solution to this bug.
In my Vuex store.js, a snapshot of the state is stored into localStorage. This snapshot is updated any time there is a change to the store itself, so the localStorage backup is always up-to-date with the actual state of the Vue.js application itself.
store.subscribe((mutation, state) => {
localStorage.setItem('store', JSON.stringify(state));
});
This store.subscribe is set in my store.js just before the export line.
A function called initStore is also defined in the store as follows
initStore(state) {
if (localStorage.getItem('store')) {
this.replaceState(Object.assign(state, JSON.parse(localStorage.getItem('store'))));
}
},
and after my store object, it is immediately called with
store.commit('initStore').

Socket.io authorization is still persistent although user is logged out on the website

I'm using the express-socket.io-session on my app. I've integrated it and it works properly. There is only one case though.
Have a look at the following code:
io.use(...);
io.sockets.on("connection", function (socket) {
socket.onVerify = (msg, fn) => {
socket.on(msg, (data) => {
console.log(socket.handshake.session.passport);
if (typeof socket.handshake.session.passport === "undefined") {
socket.emit("auth failed emit", {
msg: "Please login via the website"
});
return false;
}
fn(data, socket.handshake.session);
});
}
socket.onVerify("chat message", function (req, session) {
Chat.publish(session.email, req.msg);
});
});
Basically, on each socket request, I verify socket.handshake.session.passport being defined, which means user is logged in.
It works in this case:
I open a browser tab, login with my credentials, get redirected to /game endpoint where my game loads and connects to socket.io
I click on "logout" button, get redirected to /game endpoint, game tells me "I need to authorize."
However, it doesn't work in this case:
I open a browser tab, login with my credentials, get redirected to /game endpoint where my game loads and connects to socket.io
I click on "logout" button ON A NEW TAB, and logout in that tab.
I switch back to main tab, and game is still online.
I added a debug in my code (console.log(socket.handshake.session.passport)) and it shows { user: 1 } although I logged out already so it must be undefined
For some reason, socket.io handshake/session doesn't recognize it, so I probably need to refresh it on certain cases.
Is there any way to do it with socket.io?
This is just a try to resolve your issue, i'm not really sure it would work. But you could try something like that :
app.get("/logout", function(req,res){
//do other logging out stuff
sockets.disconnectUser(req.session.user_id);
}
// Disconnect User function
sockets.disconnectUser = function(user_id){
sockets.socket(users[user_id]).disconnect();
}
Like described here : Destroying a handshake after logout. socket.io

(Token-based A&A): send the token on every request to the server

I am debugging a token-based A&A module in node.js + angularJS. In this approach, server authenticates the user and responds with a token. This token is saved in a local memory (client side) and user sends the token on every request to the server using the attached code.
However, I face a weird situation:
I login to www.test.com
Then I can see my dashboard: www.test.com/dashboard
Next, I logout from my dashboard
Afterwards, I open a new tab and type "www.test.com/dashboard" in the address bar of the browser. I expected the user directly go to the login page. However, for 1-2 seconds I see my dashboard (e.g., www.test.com/dashboard) and then it goes to login page !!!! I dont understand why it shows my dashboard for 1-2 seconds !!!
I guess the reason is that developers did not attached the token to GET request! any suggestion to remove the problem?
btw, where is the best location (for both angularJS and EJS) to save the token to be able to send it with all requests to server (ajax, GET, ..., websocket) ?
app.factory('AuthInterceptor', function ($window, $q) {
return {
request: function(config) {
config.headers = config.headers || {};
if ($window.sessionStorage.getItem('token')) {
config.headers.Authorization = 'Bearer ' + $window.sessionStorage.getItem('token');
}
return config || $q.when(config);
},
response: function(response) {
if (response.status === 401) {
// TODO: Redirect user to login page.
}
return response || $q.when(response);
}
};
});
// Register the previously created AuthInterceptor.
app.config(function ($httpProvider) {
$httpProvider.interceptors.push('AuthInterceptor');

Resources