I'm building a CMS with Firebase, but struggling to assess whether what I require is possible, or if I'm missing something.
What I require is the ability to password-protect a page only, and remember that browser as having access. A full user account (using the in built auth) is required to edit the content of the page, but only a password is required to view it.
I know I can use the auth flow with email, but am looking for the editor to be able to create a password for viewing only.
Is this possible, or should I look elsewhere?
The way I commonly do this is a bit like Jeremy's answer, but simpler.
You ask the user for a password when they enter the page, and store that password locally (for reloads).
Then you store data in your database under a path that includes the password. So say that your password is geheim, you could store the data under:
data: {
geheim: {
value: "This is the secret value"
}
}
Now you secure your database with rules like these:
{
"rules": {
".read": false,
"data": {
"geheim": {
".read": true
}
}
}
}
Now somebody can only read the data at /data/geheim if they know the entire path. So you'll enter the data part in your code, but require them to enter geheim as the password. Then you attach a listener with:
firebase.database().ref("data").child(password).once("value", function(snapshot) {
console.log(snapshot.val());
});
And if the user entered the correct value for password, this will read the value.
Firebase Authentication only deals with authenticated user accounts. It doesn't deal with simple password protection of content.
It's definitely possible, but as Doug's answer indicated, you'll have to do it outside normal means. Off the top of my head, the way I would accomplish this is...
When a user enters a password, it stores the password in their local storage.
On page load, or on password entry... pull the password from local storage
Make a request to a Firebase cloud function, makes sure to include the password it just retrieved from local storage, and which page it is requesting content for
Firebase cloud function validates password
Firebase cloud function retrieves data for specific page
Firebase cloud function returns said data
Load data on front-end like normal
As you already identified, you should stick with the built-in Firebase auth for content editing.
I definitely suggest Frank's answer because it's simple and it works. Btw the moral of the story is that you use the firebase Database to store you view-only password but, if you want to complicate your life because you need a strong view-only password system, the Authentication product provides the custom authentication method that you can integrate with your existing auth system (for example fb login). It obviously needs a server-side implementation that is a code that takes the password, check if it's valid and sends the token back to the Auth system.
Here more details: https://firebase.google.com/docs/auth/web/custom-auth
Related
I want to implement simple authentication and authorization in an Angular project. I want to store a JWT token, and the logged in Users data, including Roles in the Local Storage. A routing guard service would check if the currentUser in the localStorage has the Roles required for the given route.
My problem is that if the user modifies the localStorageData, he could do some things otherwise he couldn't do. I understand that he can't make any valid requests to the server, because the sent token wasn't modified.
What's the solution for this?
Example:
https://stackblitz.com/edit/angular-7-role-based-authorization-example
Instructions:
Login with: Username: user Paswword: user
Execute in
console:localStorage.setItem("currentUser",'{"id":2,"username":"user","firstName":"Normal","lastName":"User","role":"Admin","token":"fake-jwt-token.User"}')
Refresh page
You can't prevent the client from doing whatever it wants with respect to itself. As long as your server is protected that's all you can do.
I was wondering if the client would be able to change the user object from null to some value (through the console) and gain access to authenticated resources without having to sign in.
I understand that you can use the following code:
auth.onAuthStateChanged(user => {
if (user) {
console.log("user signed in")
console.log(user)
} else {
console.log("user has signed out")
}
});
How does firebase counteract the client attempting to change the user object?
Is there a more secure way of handling this if this is an issue?
How would I implement it?
Thanks.
Changing the user object in the code you shared does not give the user access to additional information in the Firebase Realtime Database.
Inside the security rules for your database, information about the user is exposed through the auth variable. This variable is populated by Firebase from the ID token of the user that makes a request to the database, and it cannot be spoofed by regular users.
So while the user may change the user variable in your client-side code, this does not impact the auth variable in your server-side security rules. This is one of the reasons why it's important to secure data access server-side (like in security rules) and not just client-side.
There are two sides, Admin and Users. I am using the same database in firebase for both Users and Admin. It means that Users that are registered in my firebase authentication, can login to Admin webpage. How do i allow only specific admin's email to login into the Admin page.
For example:
Admin has an email of admin#admin.com
User has various email like 123#gmail.com, 222#gmail.com and etc.
What i want is only to allow admin#admin.com to be able to login into the Admin page but restrict login for User emails to login to the Admin page.
Using a Javascript, a simple solution would be to split the email String on the #, which will return you an Array of substrings and compare the second part which is the domain. Then, you could condition the rest of your code to work only if the criteria is true.
An example would be:
const allowedEmailDomain = 'admin.com';
const email = 'test#admin.com';
if (email.split('#')[1] === allowedEmailDomain) {
// do something, we accept this email
} else {
// return an error or do nothing
}
I make the assumption that, on the back-end side, your application is correctly secured, i.e. you have security rules in your database that only allow the users with an "admin" profile/role to read or write "admin" data in the database.
If this is not the case you MUST set correct security rules, since securing your Firebase instance only from a front-end perspective is not sufficient, as it is not very difficult to reverse engineer your app code and write some JavaScript code that will interact with your back-end. For that you could use Custom Claims.
So, with this assumption, it means that you need to adapt your front end in such a way the admin screens are only seen by the admin users.
One of the best answer is again to use Custom Claims, as explained here https://firebase.google.com/docs/auth/admin/custom-claims#access_custom_claims_on_the_client (Section "Access custom claims on the client") and in this official Firebase video: https://firebase.google.com/docs/auth/admin/custom-claims#access_custom_claims_on_the_client
Note that trying to block a user based on his email domain may not be a good solution as it is easy, in few lines of JavaScript code, to register to your Firebase project as a user with a mail like whatever#admin.com or with any other email (using createUserWithEmailAndPassword)
Just block every other domain as #admin.com.
$allowed_domains = array("admin.com");
$email_domain = array_pop(explode("#", $email));
if(!in_array($email_domain, $allowed_domains)) {
// Not an authorised email
}
It's simply. Make an array for allowed domains, explode by #, get the last element of the array and check if it exists in the allowed domains array.
there is a web application built on Vue + Firebase. The question is how to automatically enter the application after the page is reloaded, how to later log in to your database, as far as I understand after authorization, all such data remains for example
fb.auth().onAuthStateChanged(user => {
console.log(user) // user profile
}
So I can get the user's email and uid, so firebase thinks I'm an authorized user, and allows me to edit and read data with rules:
".read": "auth != null",
".write": "auth != null"
However, on the Authentication tab, there is a field for each user "Last Login Date" and it is not updated until the "Normal" authorization by email password for example.
I'll repeat the question of how to authorize in my database after rebooting the page in the browser, is there any special method for this? Thank you in advance)
There is no way for your application code or configuration to affect when Firebase updates the Last Login Date. Well... you could require your user to re-authenticate, but I doubt that is what you want, and it can be bypassed by your users.
It sounds like you want to know when the user last started you application, which is something different from when they last logged in. To track when the user starts the application, consider using an analytics package.
Alternatively you can write a "lastSeen" value to the database whenever the onAuthStateChanged fires with a user profile. Something like this would do that for the Firebase Realtime Database:
firebase.database().ref("lastSeen").child(user.uid).set(firebase.database.ServerValue.TIMESTAMP);
Is there a way to validate the response from say:
FB.api(
{
method: 'fql.query',
query: 'SELECT name, pic FROM profile WHERE id= ' + FB.getSession().uid
},
function(response) {
//...
}
);
Validating the cookie for login is easy enough using a MD5 hash and the application secret key compared to the provided sig parameter. However in the case of the api calls, what would stop someone from using firebug to change the response? Obviously this can be done on the back end for sensitive information but i'd like to keep as much of the back and forth bandwidth to Facebook on the clients end as possible.
Any thoughts?
I can't think of anything harmful the user can do other than breaking his own experience in your application UNLESS you are getting these inputs (responses) and processing them/saving them to the DB for example:
Having an input field where the user can update his FB status through it, and you want to save that to your own DB too?
In this case you would/SHOULD do the usual input validations (mysql_real_escape ..etc) anyway.
Saving the user Email?
You already can get almost all the information about the user using server-side calls once the user is authenticated and grant your application access..for instance to save the user email you shouldn't wait for the user to send it to you when you can acquire it using the email permission
Any validation you might do in JavaScript(1) would be something the user could overcome with a little JS of their own.
If you need to ensure that communications to/from Facebook are secure and not interfered with... then do it on the server.
(1) e.g.
if you had a validateFacebookResponse(resp); function... an end user simply needs to re-declare the function...
function validateFacebookResponse(resp){
return true;//always return true!
}
and any "security" you had is out the window.