Creating A 'User Only' Section In A Firebase Site - javascript

I'm hoping to be able to create a section of a site that is unable to be read/seen by anyone that's not logged in... I am using Firebase and Javascript
I have read that you are unable to set permissions for files (.htmls ect) so i wont be able to block people from seeing the pages as a whole... Ive also read that this isn't the best practice anyway... So my question is...
What is the protocol for doing this sort of thing? And how can this be done in Firebase?
I have managed to create a user only page before from a tutorial, but this was just done by hiding the content of the page with Javascript and also blocking the permissions to the displayed data through Firestore permissions.
But I don't feel this is adequate for my site as I don't want people being able to read the code in the background or get access to the page at all to begin with.
I have also read that a way to go about doing this is to use Firebase Cloud Functions to check weather the user is logged in and if they are then it spits out the code for the pages from the google servers. Is this a good idea? Or is there a better way?
Any help, tips or insights would be greatly appreciated.
Just trying to get a feel for where to begin with this problem.
Hoping there is a solution.
Thanks

Yes, its a good practice hiding or preventing the UI to be rendered for unauthorize users.
Yes, its also a good practice setting the permissions accessing your data from the database.
You should also consider middleware, navigation guards or route guards for preventing unauthorized users to visit the restricted page. It would depend on the stack, or what frontend technology you are using. You can find whatever navigation guard you chosse. For vuejs there is vue-router. Also you can use firebase authState listener. Depends on your choice.

Use firebase auth to signInWithEmailAndPassword, or whatever your authentication method was. Then, you can check the auth state in onAuthStateChange, and set your new userId state:
// somewhere...
const [currentUserId, setCurrentUserId] = useState(null)
// later..
onAuthStateChanged(auth, (user) => {
if (user && user.uid) {
setUserId(user.uid)
}
});
// even later in this component:
return (<Layout userId={currentUserId} />);
// in wherever you have links, I assumed you passed currentUserId to here:
return (
currentUserId != null ? (Give content pls) : routeToLogin();
);
Something like this should be fine and secure enough. Noone is gonna go and flip a variable in your extremely obfuscated, transpiled javascript code generated by your bundler, and even if they did find a place to flip a variable, the code would probably throw an error anyway.
You could lazy load that certain page as well once authenticated, then the code for it it wouldn't even be loaded into the users disk until they've successfully signed in.

Related

Is it possible to set expiration date for Webpush subscription?

I've read quite a lot of documentation about Webpush, and so far I've understood that push subscription should have a read-only propery expirationTime. Also, I understand how should I react if the browser decides that subscription is outdated (handle event in service worker, etc.). But is it possible to somehow set expiration date manually, without implementing complex client side logic? I guess that this is an ordinary problem for apps that have authentification.
My problem is that if user gets logged out automatically, webpush endpoint stays valid. I know multiple ways this can be solved with workarounds, but I guess that's not the optimal way for a relatively basic problem.
It's been a long time ago that I've fixed this, but I guess sharing my solution can be helpful.
The solution was to make a HTTP request from service worker to the app using fetch('/path') , because all cookies from the app are also attached to requests made from SW.
So, if user is not logged in, you are redirected to login page.
My code:
fetch('/path', {method: 'GET', redirect:'error'}).then(function(result) {
... //some code specific for my app
}).catch(function(e) {
registration.unregister(); //error on redirect to login
});

How can I Choose Specifc Users to Log Into my Javascript SPA in Azure AD?

I'm using a Javascript SPA to return a query from Microsoft Graph through the Azure AD application, and it works just fine!
The problem is when I try to loggout from the application, it says I was successfully logged out but if try to log in with another user, it logs into the previous one, in this case, me, without even asking for password.
I needed that this application could log in just few people in my organization, but anyone with "#example.com" can access my application, without the need to be signed to it or not.
I've already cleared the browser's cache and cookies and it doesn't work. Already configured the app to store the cache in the session but it also failed.
The code I'm using is available in:
https://learn.microsoft.com/en-us/azure/active-directory/develop/tutorial-v2-javascript-spa
The only differences are that I'm using another querys instead the "https://graph.microsoft.com/v1.0/me" and the permissions needed to get them.
I just needed a way to choose specific people to log into the application instead of all the organization and to fix this logout problem.
If I understood you well, I believe your solution is the option select_account.
Here is a code snippet to illustrate:
const clientApplication = new Msal.UserAgentApplication(config);
const loginRequest = {
scopes: [config.webApiScope],
prompt: "select_account",
}
clientApplication.loginPopup(loginRequest).then(function (loginResponse) {
//your code
});

Does anyone have the ability to create an account with my key?

EDIT : tried the authorized domain and it seems to be what i need, i'll try to go deeper with André's answer :)
Thank you !
Hi,
I'm new to firebase and i just finished a project but i had a question:
Since the doc says i have to put my api keys and else in the javascript, they are visible to anyone even if put into process.env
i've read here : Is it safe to expose Firebase apiKey to the public?
that making the api key public is normal and not a big deal.
I'm using the email/password auth and i'm scared
If someone takes my :
API_KEY_FIREBASE
AUTH_DOMAIN
DB_URL
PROJECT_ID
that are in the source code and use the createAccount function, is he gonna be able to create an account ?
Is yes, is there a way to disable this ?
I want to be able to create account only through the firebase console
I'm not using firebase database for my data, i only use it for auth so i don't have to create a user table in my database, but i use the IDTokens they provide to secure some routes on express.
thank you ! :)
Someone can only create an account when you have that option enabled in your firebase console. So If you have it disabled there is no problem.
You can look here in the "before you begin" section for how to enable/disable Email/password sign-in method.

Security in JavaScript Code

I am starting to build/design a new single page web application and really wanted to primarily use client-side technology (HTML, CSS, JavaScript/CoffeScript) for the front-end while having a thin REST API back-end to serve data to the front-end. An issue that has come up is about the security of JavaScript. For example, there are going to be certain links and UI elements that will only be displayed depending on the roles and resources the user has attached to them. When the user logs in, it will make a REST call that will validate the credentials and then return back a json object that has all the permissions for that user which will be stored in a JavaScript object.
Lets take this piece of javascript:
// Generated by CoffeeScript 1.3.3
(function() {
var acl, permissions, root;
root = typeof exports !== "undefined" && exports !== null ? exports : this;
permissions = {
//data…
};
acl = {
hasPermission: function(resource, permission, instanceId) {
//code….
}
};
root.acl = acl;
}).call(this);
Now this code setup make sure even through the console, no one can modify the variable permissions. The issue here is that since this is a single page application, I might want to update the permissions without having to refresh the page (maybe they add a record that then needs to be added to thier permissions). The only way I can think of doing this is by adding something like
setPermission: function(resource, permission, instanceId){
//code…
}
to the acl object however if I do that, that mean someone in the browser console could also use that to add permissions to themself that they should not have. Is there any way to add code that can not be accessed from the browser console however can be accessed from code in the JavaScript files?
Now even if I could prevent the issue described above, I still have a bigger one. No matter what I am going to need to have the hasPermission functionality however when it is declared this way, I can in the browser console overwrite that method by just doing:
acl.hasPermission(resource, permission, instanceId){return true;}
and now I would be able to see everything. Is there anyway to define this method is such a way that a user can not override it (like marking it as final or something)?
Something to note is that every REST API call is also going to check the permissions too so even if they were to see something they should not, they would still not be able to do anything and the REST API would regret the request because of permissions issue. One suggestion has been made to generate the template on the server side however I really don't like that idea as it is creating a very strong coupling between the front-end and back-end technology stacks. If for example for whatever reason we need to move form PHP to Python or Ruby, if the templates are built on the client-side in JavaScript, I only have to re-build the REST API and all the front-end code can stay the same but that is not the case if I am generating templates on the server side.
Whatever you do: you have to check all the permissions on the server-side as well (in your REST backend, as you noted). No matter what hoops you jump through, someone will be able to make a REST call that they are not supposed to make.
This effectively makes your client-side security system an optimization: you try to display only allowed operations to the user and you try to avoid round-trips to the server to fetch what is allowed.
As such you don't really need to care if a user can "hack" it: if they break your application, they can keep both parts. Nothing wrong can happen, because the server won't let them execute an action that they are not authorized to.
However, I'd still write the client-side code in a way that it expect an "access denied" as a valid answer (and not necessary an exception). There are many reasons why that response might come: If the permissions of the logged-in user are changed while he has a browser open, then the security descriptions of the client no longer match the server and that situation should be handled gracefully (display "Sorry, this operation is not permitted" and reload the security descriptions, for example).
Don't ever trust Javascript code or the front-end in general. People can even modify the code before it reaches your browser (sniffers etc) and most variables are accessible and modifiable anyways... Trust me: you are never going to be safe on the front-end :)
Always check credentials on the server-side, never only on the front-end!
In modern browsers, you can use Object.freeze or Object.defineProperty to make sure the hasPermission method cannot be redefined.
I don't know yet how to overcome the problem with setPermission. Maybe it's best to just rely on the server-side security there, which as you said you have anyway.

Why the conflicting variables?

I'm getting conflicting results between the facebook javascript SDK and the python requesthandler variables. The Javascript SDK says my user is not logged in, which is correct, while my template variable that comes from the base request handler says that my user is logged in and displays the name of the user. Is there enough info to tell what is wrong or should I paste the code I think is relevant here? A link to the login page that has the error is here. The example I used is called the runwithfriends demo app from facebook and everything with that app worked except using the logic from the app just from a website without requiring the user to be in the iframe of the app.
Plus I can't seem to get the real-time API working. I can only save userID and not refresh user data - why? I have the code but I'm not sure what's most relevant but here's some of the request handler, the relevant code is basically exactly the same as the one from the demo app:
def render(self, name, **data):
logging.debug('render')
"""Render a template"""
if not data:
logging.debug('no data')
data = {}
data[u'js_conf'] = json.dumps({
u'appId': facebookconf.FACEBOOK_APP_ID,
u'canvasName': facebookconf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.id if self.user else None,
})
data[u'logged_in_user'] = self.user #variable that is the problem
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = facebookconf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))
And even more strange, I can also get the application in a state where the javascript SDK says the user is logged in and the template variable logged_in_user says otherwise. Why are the variables conflicting?
Update: Here are screenshots from the strange login flow. I can go to my page and my name from facebook appears:
Then when I go to next page it also looks alright and has my name
But if I log out then I gets in impossible state: my name + logged out
How can I resolve this strange conflict between js and back-end?
Update: Since I only have this problem for one of my apps I can take what works from my other app and integrate. This page seems to work from my other app: http://cyberfaze.appspot.com/file/20985
Your 'user' is probably referring to the Django user not the Facebook user. Make sure you synchronize the two accounts correctly using a custom authentication backend. It's possible that the accounts get out of sync i.e. if the user switches browsers.
Keep in mind that the Facebook Python SDK will stop working after October 1st unless they update it to Oauth2.0 which is unlikely.
I just updated django-facebook-graph to work with the new authentication flow.

Categories