Firebase permission denied right after $createUser - angularjs

Inside my web-app, Firebase always tells me permission denied while things are working fine in the simulator.
FIREBASE WARNING: set at /users/simplelogin:14 failed: permission_denied
I'm having trouble adding metadata (such as a name) when users register. data is simply a JSON object that contains a name property.
var signup = function (email, password, data) {
firebaseAuth
.$createUser(email, password)
.then(function (user) {
dataRef.child('users').child(user.uid)
.set(data);
}, function (err) {
console.error(err);
});
};
My firebase rules:
{
"rules": {
"users": {
"$user": {
".read": "$user == auth.uid",
".write": "$user == auth.uid"
}
}
}
}
Simulator works fine:
Attempt to write {"name":"Test"} to /users/simplelogin:7 with auth={"uid":"simplelogin:7"}
/
/users
/users/simplelogin:7:.write: "$user == auth.uid"
=> true
Write was allowed.

As #Kato pointed out you need to log the user in yourself after registration before you can do anything.
Firebase createUser docs

Related

ReactJS location access prompt on mobile device

I am building an app in ReactJS that requires the user's location. In the browser (Chrome) on my PC I get a prompt once. If I decline, the prompt will not show again upon a page refresh. However, I will get the option to manually enable location.
When I open the same web page on my android phone in Chrome I neither get a prompt nor the option to enable location access manually. I have looked for solutions through Google but all of them rely on a prompt being automatically shown upon a navigator.geolocation.getCurrentPosition call, but it doesn't.
This is my code:
function getUserLocation() {
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(
function (position) {
setLocationDidMount(true);
console.log("got position");
},
function errorCallback(error) {
console.log(error);
navigator.permissions
.query({ name: "geolocation" })
.then(function (result) {
if (result.state === "granted") {
console.log(result.state);
//If granted then you can directly call your function here
} else if (result.state === "prompt") {
console.log(result.state);
} else if (result.state === "denied") {
console.log("location access denied by user");
//If denied then you have to show instructions to enable location
}
result.onchange = function () {
console.log(result.state);
};
});
},
{
timeout: 5000,
}
);
} else {
console.log("Not Available");
}
}
How to handle this problem? Btw, I'm not supposed to use React-Native.
As is pointed out by the article shared by Sergey, mobile browsers will only handle location properly when there is an SSL certificate in place.
While causing a warning, installing a local certificate using OpenSSL did fix the problem.

Office UI Outlook addin using auth is unstable

We're currently developing a Office UI addin using React. The addin should make a connection with a backend api and authenticate the user using bearer tokens. The backend api is protected by Azure AD.
We based our solution on the example that is offered by Microsoft: https://github.com/OfficeDev/PnP-OfficeAddins/tree/master/Samples/auth/Office-Add-in-Microsoft-Graph-React This uses msal.js for the authentication.
The login dialog is opened like so:
await Office.context.ui.displayDialogAsync(dialogLoginUrl, { height: 40, width: 30 }, result => {
if (result.status === Office.AsyncResultStatus.Failed) {
displayError(`${result.error.code} ${result.error.message}`);
} else {
loginDialog = result.value;
loginDialog.addEventHandler(Office.EventType.DialogMessageReceived, processLoginMessage);
loginDialog.addEventHandler(Office.EventType.DialogEventReceived, processLoginDialogEvent);
}
});
And the following code runs within the dialog:
import { UserAgentApplication } from "msal";
(() => {
// The initialize function must be run each time a new page is loaded
Office.initialize = () => {
const config = {
auth: {
clientId: "",
authority: "",
redirectUri: "https://localhost:3000/login.html",
navigateToLoginRequestUrl: false
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: false
}
};
const userAgentApp = new UserAgentApplication(config);
const authCallback = (error, response) => {
if (!error) {
if (response.tokenType === "id_token") {
localStorage.setItem("loggedIn", "yes");
} else {
// The tokenType is access_token, so send success message and token.
Office.context.ui.messageParent(JSON.stringify({ status: "success", result: response.accessToken }));
}
} else {
const errorData = `errorCode: ${error.errorCode}
message: ${error.errorMessage}
errorStack: ${error.stack}`;
Office.context.ui.messageParent(JSON.stringify({ status: "failure", result: errorData }));
}
};
userAgentApp.handleRedirectCallback(authCallback);
const request = {
scopes: ["api://..."]
};
if (localStorage.getItem("loggedIn") === "yes") {
userAgentApp.acquireTokenRedirect(request);
} else {
// This will login the user and then the (response.tokenType === "id_token")
// path in authCallback below will run, which sets localStorage.loggedIn to "yes"
// and then the dialog is redirected back to this script, so the
// acquireTokenRedirect above runs.
userAgentApp.loginRedirect(request);
}
};
})();
Unfortunately this doesn't seem lead to a stable addin. The authentication dialog sometimes works as expected, but sometimes it doesn't. In Outlook on macOS it seems to work fine, but in Outlook on Windows the handling of the callback is not always working correctly. Also in the web version of Outlook it doesn't work as expected.
The question is whether someone has a working solution using React and msal.js in a Outlook addin.

Firebase: Authenticated user with full permissions gets PERMISSION DENIED on root

Set rules in Firebase console:
{
"rules": {
".read": true,
".write": true
}
}
Run in Chrome console:
> firebase.database().ref().set('foo');
Database tab of Firebase Console now reflects foo.
Set rules in Firebase console:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Run in Chrome console:
> firebase.auth().signInAnonymously()
< A {W: 1, sa: undefined, u: null, oa: null, fb: nul…}
> firebase.database().ref().set('bar');
Uncaught (in promise) Error: PERMISSION_DENIED: Permission denied
Firebase is set up to allow anonymous and Google users, both of which get PERMISSION DENIED when I attempt to read or write.
EDIT
I've added an example project here:
https://github.com/formido/react-firebaseui-web-stackoverflow-question
If I set my rules to:
{
"rules": {
".read": "auth != null",
"44358340": { ".write": true }
}
}
...and log in, the console will print Timestamp written.
If I set my rules to:
{
"rules": {
".read": "auth != null",
"44358340": { ".write": "auth != null" }
}
}
...and log in, the console will print PERMISSION DENIED.
The most relevant part of the code I'm aware of is:
componentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
const ref = firebase.database().ref("/44358340");
if (user) {
const timestamp = firebase.database.ServerValue.TIMESTAMP;
ref.set(timestamp).then((error) => {
if (error) {
console.error(error);
}
else {
console.log('Timestamp written');
}
});
}
}
this.setState({loading: false, user});
});
}
It's hosted on an Amazon server. I thought maybe I had to set the AUTH_DOMAIN to the AWS host, but that didn't make a difference.
It was a bug. See discussion starting here.
Fix here and it was deployed in Firebase 4.1.2.

Client doesn't have permission to access the desired data in Firebase

I have a page that is calling addCheckin() method which is inside a controller. In the controller, I am trying to create a reference as follows:
var ref = firebase.database().ref("users/" + $scope.whichuser + "/meetings/" +$scope.whichmeeting + "/checkins");
$scope.whichuser and $scope.whichmeeting are the $routeParams that I am passing from another route.
Here's my checkin controller-
myApp.controller("CheckinsController",
['$scope','$rootScope','$firebaseArray','$routeParams','$firebaseObject',
function($scope,$rootScope,$firebaseArray,$routeParams,$firebaseObject){
$scope.whichuser = $routeParams.uid;
$scope.whichmeeting = $routeParams.mid;
var ref = firebase.database().ref("users/" + $scope.whichuser + "/meetings/" +$scope.whichmeeting + "/checkins");
$scope.addCheckin = function(){
var checkinInfo = $firebaseArray(ref);
var data={
firstname:$scope.firstname,
lastname:$scope.lastname,
email:$scope.email,
date:firebase.database.ServerValue.TIMESTAMP
}
checkinInfo.$add(data);
}
}]);/*controller*/
There are two errors that I am getting here-
Error 1:
Error: permission_denied at /users/Vp2P1MqKm7ckXqV2Uy3OzTnn6bB3/meetings: Client doesn't have permission to access the desired data.
Error 2:
Error: permission_denied at /users/Vp2P1MqKm7ckXqV2Uy3OzTnn6bB3/meetings/-KT5tqMYKXsFssmcRLm6/checkins: Client doesn't have permission to access the desired data.
And this is what I am tring to achieve-
Go to Firebase console of your app
Select Database From Side Menu --> Select Rule From tabs above --> Update your rule like this
{
"rules": {
".read": true,
".write": true
}
}
hope it solve your problem . thanks :)
Firebase project by default starts with Firestore as database.
This issue can happen if the application uses "Firebase Realtime Database" and permission for it are not configured. Read and write permission for Firebase Realtime Database should be explicitly granted.
To do so, in Firebase console, Database > Pick "Realtime Database" instead of "Firestore Beta" from the dropdown beside Database > Rules > Set
{
/* Visit https://firebase.google.com/docs/database/security to learn more about security rules. */
"rules": {
".read": true,
".write": true
}
}
Hope that help!
As all provided answers include a security issue where everyone could write / delete entries in your database, which for instance could cause extensive costs and or complete loss of data, when used in a bad way.
Of course you need to use firebase authentication features to use those rules, but preventing write access for anonymous should be default. The following rule provides read access to everyone while keeping security.
{
"rules": {
".read": true,
".write": "auth.uid != null"
}
}
None of these answers provide the most secure way to set up the Realtime Database.
You don't want anyone to randomly access or write to your database
Even if the user is authenticated, you don't want them to access other's data
This rule should address all the cases:
{
"rules": {
"users": {
"$uid": {
".read": "$uid === auth.uid",
".write": "$uid === auth.uid"
}
}
}
}
Please register your self in firebase by using the below code
Firebase.auth()
.createUserWithEmailAndPassword(email, password)
.then((data) => console.log(data))
.catch(error => console.log(error))
and authenticate your self using registered email and password by below code
Firebase.auth()
.signInWithEmailAndPassword(email, password)
.then((data) => console.log(data))
.catch(error => console.log(error))
With the below rules in your real time database
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Then automatically you will be able to access the data from the real time database
var ref = firebase.database().ref("users");
ref.on("value", function(snapshot) {
snapshot.forEach(function(childSnapshot) {
var childData = childSnapshot.val();
var id=childData.id;
console.log(childData);
});
});
While logging out add the below command to logout of the app
Firebase.auth().signOut()
In order to grant access to all authenticated users, go to database rules tab in firebase web console and copy/paste following rules:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
It appears that the /users/{uid} routes can be written to but they can not be read from. I changed /users to /userx and it immediately began working.
I hope this can help you.
{
".read": true,
".write": "auth !== null && root.child('users').child(auth.uid).child('meetings').val() === true"
}
You can remove the string && root.child('users').child(auth.uid).child('meetings').val() === true"
and the result is the same.
You can also specify the time, till that you wanna allow like this:
Select Database From Side Menu --> Select Rule From tabs above --> Update your rule like this
{
"rules": {
".read": "now < 1672384350000", // 2022-12-30
".write": "now < 1672384350000", // 2022-12-30
}
}
If someone still get the Error: permission_denied after allowing correct read rights and are fetching data with some kind of firebase npm package; it could be because you're trying to read from Realtime Database instead of the Cloud Firestore.
I was using react-redux-firebase npm package for a quick and easy setup. By default it uses the Realtime Database. If you're using Cloud Firestore you need to state that in the config with useFirestoreForProfile set to true.
import { ReactReduxFirebaseProvider } from 'react-redux-firebase';
const store = configureStore();
const rrfProps = {
firebase,
config: {
userProfile: 'users',
useFirestoreForProfile: true // Firestore for Profile instead of Realtime DB
},
dispatch: store.dispatch,
createFirestoreInstance,
};
Probably similar issues with other packages for firebase, like flamelink that support Realtime Database but not Cloud Firestore as stated in this issue.

How do you prevent duplicate user properties in Firebase?

I'm Using FirebaseSimpleLogin to create users and handle authentication.
When I try and create a new user with simple login via the $createUser() method, firebase won't create the user if the email address has already been used. However, I am also using $set() to save my created users to my firebase after I create them and I am using user.uid as the key. When trying to write to the database, firebase will save the record even if the username is not unique since only email and password are required for simple login. So how can I validate a username to be unique when it is not being used as the key to the user object?
I'm creating new users like this:
$scope.createUser = function() {
$scope.auth.$createUser('trinker#gmail.com', 'password').then(function(user, err) {
if (!err) {
ref.child('users/' + user.uid).set({
email: user.email,
username: user.username
});
console.log("success!");
}else{
console.log(err.message);
}
});
}
And my user object looks like this:
{
"users" : {
"simplelogin:28" : {
"email" : "trinker#gmail.com",
"username" : "jtrinker"
},
"simplelogin:30" : {
"email" : "test#gmail.com",
"username" : "jtrinker"
}
}
}
}
I need to use the uid as the key for each user, but I still need the username to be unique.
How can I can I prevent firebase from saving records if properties within one object are not unique to properties inside a different object?
First of all, if users already have a username, it's unique, and this is not going to go away, I'd recommend that you give up on using simple login uids. This is going to create nothing but issues trying to flip back and forth between the two, as you've already discovered here. Investigate creating your own tokens with a tool like firebase-passport-login and then store the records by username.
But since that wasn't your question, let's resolve that while we're here, since you may want to go ahead and enter the thorny briar of dual identities through which I have passed many times.
To make the username unique, store an index of usernames.
/users/$userid/username/$username
/usernames/$username/$userid
To ensure they are unique, add a security rule as follows on the user id in usernames/ path, which ensures only one user can be assigned per username and that the value is the user's id:
".write": "newData.val() === auth.uid && !data.exists()"
Now enforce that they match by adding the following to the username in the users/ record:
"users": {
"$userid": {
"username": {
".validate": "root.child('usernames/'+newData.val()).val() === $userid"
}
}
}
This will ensure the ids are unique. Be careful with read privileges. You may want to avoid those entirely since you don't want anyone looking up private emails or usernames. Something like I demonstrated in support for saving these would be ideal.
The idea here is that you try to assign the username and email, if they fail, then they already exist and belong to another user. Otherwise, you insert them into the user record and now have users indexed by uid and email.
To comply with SO protocol, here's the code from that gist, which is better read via the link:
var fb = new Firebase(URL);
function escapeEmail(email) {
return email.replace('.', ',');
}
function claimEmail(userId, email, next) {
fb.child('email_lookup').child(escapeEmail(email)).set(userId, function(err) {
if( err ) { throw new Error('email already taken'); }
next();
});
}
function claimUsername(userId, username, next) {
fb.child('username_lookup').child(username).set(userId, function(err) {
if( err ) { throw new Error('username already taken'); }
next();
});
}
function createUser(userId, data) {
claimEmail(userId, data.email, claimUsername.bind(null, userId, data.username, function() {
fb.child('users').child(userId).set(data);
);
}
And the rules:
{
"rules": {
"users": {
"$user": {
"username": {
".validate": "root.child('username_lookup/'+newData.val()).val() === auth.uid"
},
"email": {
".validate": "root.child('email_lookup').child(newData.val().replace('.', ',')).val() === auth.uid"
}
}
},
"email_lookup": {
"$email": {
// not readable, cannot get a list of emails!
// can only write if this email is not already in the db
".write": "!data.exists()",
// can only write my own uid into this index
".validate": "newData.val() === auth.uid"
}
},
"username_lookup": {
"$username": {
// not readable, cannot get a list of usernames!
// can only write if this username is not already in the db
".write": "!data.exists()",
// can only write my own uid into this index
".validate": "newData.val() === auth.uid"
}
},
}
}
Wouldn't it be easier to just use the security rules to check for it's existence? I have mine set up as follows:
"usernames": {
"$usernameid": {
".read": "auth != null",
".write": "auth != null && (!data.exists() || !newData.exists())"
}
}
This allows the write if the username doesn't exist. I believe I got this directly from the Firebase docs.

Resources