I have created an app with Electron that allows users to sign-in and sign-out.
On logout, I delete all information stored in keytar (access tokens, etc.)
I then call oAuth2Client.revokeCredentials() when logging out.
const { google, GoogleApis } = require('googleapis')
const oAuth2Client = new google.auth.OAuth2(
credentials.client_id,
credentials.client_secret,
credentials.redirect_uris[1]
)
async function logout() {
console.log('Logging out....')
await keytar.deletePassword(keytarService, keytarAccount)
await oAuth2Client.revokeCredentials()
}
However, when the user opens the app again, and they get the login screen, their Google Account is still there where all they have to do is click on it. They don't have to login again, they don't have to enter a password, and they don't have to use 2FA again.
What I am looking for is, when a user logs out of my Desktop Application, they are completely Logged out with no Google remnants left behind, such that, when they open the Application the next time, they have to re-enter a user name and a password and use their 2FA.
The reason behind the users seeing their google account logged in, is that the users didn't logout of their google account, so all the session cookies are stored in your renderer and revoking the tokens in your main process will not revoke the tokens in your renderer process. In order to clear the users session data from your renderer, what you can do is that, you can clear the storage from that renderer as shown below.
// Before quitting the window, clear the storage so all the cookies related to google are cleared
AuthWindow.webContents.session.clearStorageData().then(function (data) {
AuthWindow.destroy();
});
Next time when the users opens their app again, they will need to login their account using their credentials as all the session data related to their account is cleared now.
Related
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.
I am building a login page in my React app using firebase (sign in with google redirect the user method)
and it is working but it takes almost two seconds for firebase to get the current user, which is not the best for UX.
here is the code
componentDidMount(){
console.log(Date.now());
const auth = getAuth();
this.authListener = onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in
console.log(Date.now());
this.props.dispatch(addUserAction(user.email))
}
else {
// User is signed out
this.props.dispatch(addUserAction(null))
}
});
}
what i get in my console is that the difference of time is 1768 milliseconds which is almost 2 seconds,
am i doing something wrong ?
the console is showing the difference of time as 2 seconds
When you restart the app/reload the page, Firebase automatically restores the user's authentication state based on the information it stored in local storage when the user first signed in. For this it does make a call to the server though, to check whether the credentials are still valid - and for example to ensure the account hasn't been disabled. It's likely that this call is what is taking time in your use-case.
A common trick is to make your own determination on whether the server-check is likely to succeed based on only client-side information. For this, store an extra value in local storage when the user signs in successfully, say isAuthenticated. Now when you reload the page/app, you an read this value from local storage, and then the user was previously authenticated, assume that they will be authenticated again.
The assumption may be wrong of course, so you'll have to handle that scenario too in your code.
Also see this talk Architecting Mobile Web Apps, where Michael Bleigh talks about the technique.
The problem:
Using msal v2, when user log in to the app via Microsoft account, it saves params to the sessionStorage and it all works great, problem happens when user logs out in the Office.com or any other site using Microsoft SSO. Since the data is still saved in sessionStorage (tried same with localStorage) the AcquireSilentToken(...) resolves with the cached data, even though the user has been logged out.
Tried How to know if a given user is already logged in with MSAL?
It suggest using AcquireSilentToken(...) but it resolves promise without error since it checks sessionStorage.
My case:
In the middleware I would like to do:
const promise = msalInstance.acquireTokenSilent(graphScopes);
promise.then(resp=>{
//User is logged continue next();
}).catch(error=>{
//User is not logged in clear sessionStorage/localStorage and next('/login')
});
So if anyone can help me with the way of asking the thru msal if user has logged out. I would really appreciate it.
This behavior is by design. AAD services uses cookies to remember who you are and to automatically sign you in.
The sign-out process for services forces the session cookies to expire. These session cookies are used to maintain your sign-in state when you use these services. However, because the web browser is still running and may not be updated to handle cookies correctly, you may have a cookie that is not updated to expire and finish the sign-out process. By default, these cookies are valid for eight hours or are set to expire when you close all web browsers.
const promise = msalInstance.acquireTokenSilent(graphScopes);
promise.then(resp=>{
const logoutRequest = {
account: instance.getAccountByHomeId(homeAccountId),
postLogoutRedirectUri: "your_app_logout_redirect_uri"
}
instance.logoutRedirect(logoutRequest);
}).catch(error=>{
//User is not logged in clear sessionStorage/localStorage and next('/login')
});
Also this is a known issue.
I have managed to setup a custom login system using Firebase. The user enters email/password and is redirected to the main page(which is private). I am having issue with onAuthStateChanged after logging in. When I check the auth state after logging into the main page, i get invalid user (null). The firebase dashboard shows I have logged in successfully but onAuthStateChanged is the opposite.
I am trying to check if a user is logged in to my html pages, if not I want to redirect them to the login page. I like how the authentication works in firebase but I need to protect my html pages not my divs (which is what the vast majority of firebase auth tutorials show).
If anyone has an easier way to password protect a web directory that looks nicer than HTaccess, please advise (I am not crazy about using wordpress for password protection, but its an option). Otherwise, I guess I will have to do this in PHP. Thanks in advance!
(function () {
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in.
console.log(user);
console.log('A user is logged in.');
} else {
// No user is signed in.
console.log('Invalid user. Redirecting to root.');
window.location.replace('../index.html');
}
});
})();
If you navigate to a new page, Firebase will have to initialize again. As part of that it will try to restore the user's authentication state, but this requires a call to the server which takes time.
For that reason the onAuthStateChanged listener will initially fire with null as the current user (and auth.currentUser is set to null). Then once the user is signed in, the listener fires again with the user object for that user.
If you want to detect this initial state, you can either store some token value in the browser's local storage that you can check for yourself on the new page, or you could set a time-out for when you expect the user to be re-signed in and only then navigate away to the index.html page.
I'm using firebase.auth().signOut() to log a user out of his Google account. However, when I the user logs in again using firebase.auth().signInWithPopup(provider) (const provider = new firebase.auth.GoogleAuthProvider()), he immediately gets logged in to the Google account he was last logged in with. No password request, no UI, nothing. Just a short loading animation and the user is logged in again.
What do I have to do so the user can switch to a different Google account? At the moment it's simply not possible.
A little late response but hope it helps.
You can add a custom parameter to the auth provider to force picking an account each time.
const provider = new firebase.auth.GoogleAuthProvider();
provider.setCustomParameters({
prompt: 'select_account'
});
firebase.auth().signInWithPopup(provider)