I saw a question at this link because I have a similar situation and I wanted to comment but unfortunately I do not have enough rep to do so. I would like some clarification on it.
in Tor Arne Falk's question : He displayed a script that he used to manage logged in users and logged out users like so ⬇️
<script>
const auth = firebase.auth()
auth.onAuthStateChanged((user) => {
if (user) {
document.getElementById("body").style.display = "block";
} else {
window.location.replace("/login.html");
}
});
</script>
My question is do you put that script in every html after auth ( & except auth) to check if a user is logged in or not to display content in that page after logging in or redirect them to the auth page
further more Firebase is a type="module" so a common issue with modules is that we cant import firebase outside a module meaning we cant read data from the firebase.js (handling auth & database) to a different script, example: using Tor Arne Falk script (as is) on different html pages for the same cause I believe it will not work.
please share some ideas on if it possible to read data outside the module I would like an example to help me get started if not what should I do within my firebase.js file to ensure a user is logged in across every other page to prevent them from hitting the back button and seeing that that they are still logged in or link jumping (skipping my auth page and going to a page only logged in users are supposed to see without logging in) I hope I make sense!🤔
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 having a react application with firebase as authentication. My authentication code is below
await firebase.auth().onAuthStateChanged((user) => {
if (user) {
props.setUser(user); //setting the user if login/register happens
history.push(`/admin/dashboard`);
console.log("user",user)
} else {
props.setUser(null); //blocks the user to get into the app if he/she is not logged in
history.push("/");
}
});
So, when user logs in..he will be navigated to /admin/dashboard. suppose when am in /admin/home and when i refresh the page, it goes again to admin/dashboard which shouldn't happen. so I tried
history.push(${props.location.pathname}); it works correctly after the refresh, it stays on the same page when the application is logged in. but when I restart the server again when I try to log in, it says no redirect url is specified. Got stuck on this for a long time.. Any help is welcome.Thanks
What your code does is check if the user is logged in and only let the user access the data if so.
You should do that in the fireabse rules (= serverside) as this is way more secure.
You didn't provide the kind of FirebaseDB you are using. So assuming you use the Realtime Database here are some according rules:
{
“rules”: {
“.read”: “auth != null”,
“.write”: “auth != null”
}
}
You should maybe check the rules before deploying your app, because now every authenticated user can change/add/delete data, but you get the point. This does exactly what you want so you won't even need to perform a check in your ReactJS App. Firebase will automatically deny unauthenticated users the access to the database.
Btw: You should try to implement security relevant things in the Firebase Rules. Ideally you want your rules to be written in a way that you don't need to perform any validation inside your ReactJS app. Firebase rules can get quite complex. I experienced that myself when writing a chat app with chatrooms and everything. But it is definitly worth the effort if your app is more secure after.
I have a ASP.net website, that uses MSAL to login.
The issue I keep having is that, whenever the user logs in, then logs out again the user is redirected to a logout page.
This page implements the new Msal.UserAgentApplication(msalConfig).logout() function, and redirects the user back to the login page.
This all works perfectly. The login page will then automatically redirect the user back to the AAD login page.
If the user then decides to login again, the result of MyMsalObject.GetAccount() returns null, and an error occurs that mentions the following:
ClientAuthError: Login_In_Progress: Error during login call - login is already in progress.
At first I used one js file to handle log in & logout, I then realised that that probably wasn't he best solution, as it attempted a login on load.
So I decided to split them up into two separate JS files but this hasn't fixed my problem.
msalObject definition:
var msalConfig = {
auth: {
clientId: "my_client_id",
authority: "my_authority_url",
redirectUri: "http://localhost:port"
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
var myMSALObj = new Msal.UserAgentApplication(msalConfig);
login code:
$(document).ready(function(){
if (!myMSALObj.getAccount()) {
myMSALObj.loginRedirect(msalConfig);
acquireTokenRedirectAndCallMSGraph();
}
});
Edit:
Some extra details.
I've now made it so that users must click on a button before being redirected to Microsoft to login.
The above unfortunately still applies. After logging in succesfully for the first time & logging out, a secondary login attempt will not yield a value in the function getaccount() even though, it works perfectly the first time.
The error I get after I've logged in is still the same namely:
ClientAuthError: Login_In_Progress: Error during login call - login is already in progress.
Even though I just logged in..
Does anyone have a solution?
Edit 2:
There is a little bit of progress.. sort of, I've been able to fix the error above by changing way I log the user out.
The config file is now a definition within document ready & I've moved the log out function in there aswell.
Although I now face a new challenge..
Refused to display 'https://login.microsoftonline.com/{{}}' in a frame because it set 'X-Frame-Options' to 'deny'.
And im not entirely sure if this is a step forward or backwards. The reproduction scenario remains the same, you log in, then you log out & back in again, when microsoft sends the user back to the login page I get the error mentioned in this edit, but I don't get this error on the 1st login attempt.
The answer stated on: https://github.com/AzureAD/microsoft-authentication-library-for-js/wiki/FAQs#q6-how-to-avoid-page-reloads-when-acquiring-and-renewing-tokens-silently doesn't help at ALL, I'm using chrome but it still doesn't work..
Check session/local storage and cookies for any dangling msal information. We had the same problem, and I stumbled into this link.
https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/1069
It suggests clearing storage and cookies, but if you dig into using your browser tools, you'll see several entries like "msal...interactive...". Those will have values of "in progress", and that's what is causing the error.
Deleting those entries clears up the problem.
First, when using loginRedirect, you need to put the code you want to run when the user is redirected back to your app inside myMsalObj.handleRedirectCallback(callback) instead of inside the function where you initiate the redirect process. Also note, that handleRedirectCallback should be registered on initial page load.
Example:
const myMSALObj = new Msal.UserAgentApplication(msalConfig);
$(document).ready(function(){
myMSALObj.handleRedirectCallback(function(error, response) {
var account = myMSALObj.getAcccount();
if (account) {
// user is logged in
}
});
});
I am a little confused about your app though. Are you saying you have page in your app that just calls myMSALObj.logout()?
I am not really a web app developer and I would like to ask about best practices for gating website content.
I am preparing to deploy documentation created with mkdocs. It uses Netlify Identity because with that Github auth is available without any coding.
My current solution: I have added the Netlify Identity script in head and the login/logoff button via template addons in mkdocs, and then created a static document /login/ (that gets picked up automatically in mkdocs but does not get generated with template).
In the standard template there is a JS redirect to /login/ unless user is logged in:
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
document.location.href = "/login/";
}
});
}
On the static page there is a redirect to / only just after user has logged in:
if (window.netlifyIdentity) {
window.netlifyIdentity.on("init", user => {
if (!user) {
window.netlifyIdentity.on("login", () => {
document.location.href = "/";
});
}
});
}
I hope this is a reasonable way to go about it. The docs do not store anything critical but I still wouldn't want that content exposed.
But I have noticed on slow connection the redirect takes a second or two so when a deep URL is accessed the content flashes on the screen before login.
What can be done to stop this and load the content only after the login check is performed?
This is not going to work as you desire and is not secure.
If I wanted to read your content without an account, I could simply disable JavaScript in my browser (a few mouse clicks) and your site would load, but the redirect would never run.
Regardless, with JavaScript enabled, the way it works is that the browser downloads the page, then downloads any resources (including scripts), and then finally runs any scripts. There is no way to change that. Of course, on a fast system, the user may not perceive a delay, as the delay is very short, but there is always a delay. That is how browsers work.
If you don't want your users to have access to the information until after they are logged in , then you must not send the information out until they are logged in. In other words, you need to configure your server to not send the page at all until it receives verification that the user has permission to receive that information. How you do that depends on which server you are using among other things, which would be the subject of a separate question.
I know this is a old post but you can use netlify functions combined with a netlify redirect file.
You would have to set a role of a user when signing up using the metadata, you could do this with a netlify function thats hooked into netlify identity, more here.
Create a function called identity-signup.js when a user signs up this function is automatically called.
exports.handler = async (event) => {
const { user } = JSON.parse(event.body)
// you could do something with the user here: eg console.log(user.email)
// or using stripe: const customer = await stripe.customers.create({ email: user.email });
return {
statusCode: 200,
body: JSON.stringify({
app_metadata: {
roles: ['free']
}
})
}
}
Once you have a role you can simply create a _redirects file like so:
/authedcontent/* 200! Role=free
/authedcontent/ / 404
Later down the line you can extend the netlify function to save the users detail in an external database or maybe setup a stripe subscription.
The only caveat is that this requires a paid netlify account.
I have a Facebook Connect site using the Javascript API - I'm not using any FBML tags. It was working fine until a couple of days ago and now I have a problem with reloading the page while the user is logged in.
The user can log in fine, and I can get the user's Facebook ID. They can refresh the page and they're still logged in (and I still get the ID). But if they refresh the page again (and subsequently), then FB.Connect.get_loggedInUser() always returns 'None', rather than the Facebook ID, even though FB.Connect.get_status().waitUntilReady() has said they're logged in.
Here's my basic code... can anyone see anything wrong?
FB_RequireFeatures(['Api'], function() {
FB.init('MY_API_KEY', '/xd_receiver.htm', {});
FB.ensureInit(function() {
FB.Connect.get_status().waitUntilReady( function( status ) {
switch (status) {
case FB.ConnectState.connected:
FB.Connect.requireSession(function() {
if (FB.Connect.get_loggedInUser()) {
var uid = FB.Connect.get_loggedInUser();
// Some more stuff here with the user's ID, displaying info in the page, etc.
}
}
break;
case FB.ConnectState.appNotAuthorized:
case FB.ConnectState.userNotLoggedIn:
// Display FB Connect button in page.
}
});
});
});
Is there something wrong with that? I can't work out how to ensure I get the user's logged in ID. Many thanks.
So, after much testing with various apps and domains and... it seems there was some conflict going on between the JavaScript Facebook code and some pyFacebook code in the Django back-end. Some confusion between the sessions stuff (yet to be figured out) was causing Safari to throw errors. So, we don't know the solution, but the JavaScript code above should, on its own, work fine.