Uploading to Firebase - firebase

I have a MySQL application and would like to moved it gradually to Firebase. As a first step I am "dumping" JSON objects made from queries (simple queries over one or two tables) to Firebase. I want to do this on a regular basis, just calling a page that produces an object (from the MySQL using Coldfusion) and as a security rule I should be logged in with a specific Twitter account.
This is the page READING data Firebase http://www.redesign.mobi/fbse/
This is the page in WRITE mode http://www.redesign.mobi/fbse/?up=aktuell
Presently I cannot write because I set write to false in "Security and Rules":
".write": false
Just for testing I wanted to set the rule that anyone logged in can write
".write": "auth !== null && auth.provider === 'twitter'"
The Twitter auth works properly but it does not prevent a not-logged-in user from writing.
What did I overlook?
Here is the code for auth and writing:
<!--- Auth --->
var ref = new Firebase("https://shining-fire-8148.firebaseio.com");
ref.authWithOAuthPopup("twitter", function(error, authData) {
if (error) {
console.log("Login Failed!", error);
} else {
console.log("Authenticated successfully with payload:", authData);
}
});
var qaktuell = new Firebase("https://XXXXXXXXX.firebaseio.com/angebote3");
qaktuell.set($scope.items);

Related

Firebase Realtime database listeners behave differently depending on the security rules

I'm currently getting an unexpected behaviour from the Firebase Realtime database. To reproduce it I've used exactly the code below only using the Firebase JavaScript SDK (7.19.1) with Vanilla JS.
Code:
Stays the same for both versions.
Only the security rules will be changed directly in Firebase.
const CONFIG = { ... }; // TODO insert your config
const AUTH_TOKEN = "eyJ0..." // TODO insert your auth token (JWT)
firebase.initializeApp(CONFIG);
firebase
.auth()
.signInWithCustomToken(AUTH_TOKEN)
.then(() => {
console.log("authentication successful");
performFirebaseOperations();
})
.catch((error) => {
console.log("authentication failed", error);
});
const performFirebaseOperations = () => {
const database = firebase.database();
const allUsersRef = database.ref("users");
const myUserRef = database.ref("users/1");
allUsersRef.on("child_added", (data) => {
console.log("child_added", data.val());
});
allUsersRef.on("child_removed", (data) => {
console.log("child_removed", data.val());
});
myUserRef
.update({
name: "John",
})
.then(() => {
console.log("update success");
})
.catch((error) => {
console.log("update error", error);
});
};
Version 1
Security rules:
{
"rules": {
".write": false,
"users": {
".read": "auth !== null",
"$userId": {
".write": false,
}
}
}
}
Console output:
Version 2
Security rules:
{
"rules": {
".write": false,
".read": false
}
}
Console output:
In both version nothing is ever written to the Firebase database because the security rules will not allow this.
This article explains that the Firebase Realtime database operations are optimistic which explains why child_added is displayed in the console even though it's never written to the database. From my understanding Version 1 is the expected behaviour. But why doesn't Version 2 show the same behaviour even though I've only changed the security rules? I thought the update is optimistic without going to the server first, so I expected a child_added event.
I think I figured it out, but it's another edge case.
I was able to get the same result with the rules that allow writing, by making the client go offline before any other operation.
firebase.database().goOffline();
At that point the behavior with your first set of rules is the same as with the second set of rules.
My testbed, although you won't be able to change my rules: https://jsbin.com/guzowak/edit?js,console
Given this...
It comes down to a guarantee that the Firebase client makes to never show you a partial snapshot.
In your first case the flow is as follows:
Your client listens to /users (for both child_added or child_removed, but either of them would be enough for this step), so it gets a snapshot of the data for all users.
You then perform a write to /users/1, which is a modification of a node the client already knows, so it can fire a local event for that change.
In your second case, the client never gets the data for /users in step 1, so it can't fire the event in step 2.

not able to insert record in firebase table using Angular 6

not able to insert record in firebase table using Angular 6, I am using real-time database
Component code below:-
*CreateRecord() {
let jsonob: jsonob = {
'MSG-21A7605D-A48E-4291-ABDA-4DFE046FE597': {
msg_text: 'Test Person1'
}
};
this.crudService.create_NewStudent(jsonob).then(resp => {
this.msg_text = "";
console.log(resp);
}).catch(error => {
console.log(error);
});
}
create_NewStudent(record) {
return this.firestore.collection('messages').add(record);
}
Firebase Rules is below:-
{
"rules": {
".read": true,
".write": true
}
}
messages table is below:-
https://i.stack.imgur.com/InWGE.png
Error Message:
FirebaseError: Missing or insufficient permissions.
at new FirestoreError (http://localhost:4200/vendor.js:93048:28)
Your question says "I am using real-time database", but the code is using Firestore. On top of that, you're showing rules for Realtime Database, which in no way affect access to Firestore. The error message "Missing or insufficient permissions" is suggesting that your Firestore security rules (not your Realtime Database rules) are disallowing access.
Basically, you're going to have to go back and make sure you're using the correct database system across the board.

Update document with new userId after user authenticated for the second time

I have the following use case that is working as expected:
new user arrives on website
user is given an user.uid from anonymous sign in from firebase
user creates a document having as userId reference the aforementioned user.uid
in the very same page user is invited to sign in otherwise the document will be lost
user logs in and finds in is account the document
WIN!
Now I have a use case that is not working as expected:
returning user with session expired or from a different browser arrives on website
user is given an user.uid from anonymous sign in from firebase
user creates a document having as userId reference the aforementioned user.uid
in the very same page user is invited to sign in otherwise the document will be lost
user logs in and doens't find in is account the document
DIDN'T WIN THIS TIME :(
I configured firebase auth with the following configuration:
const uiConfig = {
signInFlow: 'popup',
autoUpgradeAnonymousUsers: true,
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
],
callbacks: {
signInFailure: (error: any) => {
if (error.code != 'firebaseui/anonymous-upgrade-merge-conflict') {
return Promise.resolve();
}
var cred = error.credential;
return firebase.auth().SignInAndRetrieveDataWithCredential(cred);
}
},
};
So, as you can see, the issue is that, the first time, autoUpgradeAnonymousUsers create a new userId with the anonymous user id and everything is fine, but of course the second time doesn't work anymore.
How should I solve this problem given that in my security rules I want to create a check that userId cannot be updated AND only a request with the same userId can see the document?
security rules:
allow create: if request.auth.uid != null
allow read: if request.auth.uid == resource.data.userId
&& request.auth.uid != null
allow update: if request.auth.uid == request.resource.data.userId && resource.data.userId == request.resource.data.userId && request.auth.uid != null
Thank you.
The problem is you can not create a new user with the same credentials. If the user logs in he looses the data from the anonymous sign in.
You have to save the data from the anonymous user locally and after the user logs in you have to copy the data to the current user. You should also deleting the anonymous account.
I have found this example which uses the Firebase realtime database to save the user data.
https://github.com/firebase/firebaseui-web#upgrading-anonymous-users
// signInFailure callback must be provided to handle merge conflicts which
// occur when an existing credential is linked to an anonymous user.
signInFailure: function(error) {
// For merge conflicts, the error.code will be
// 'firebaseui/anonymous-upgrade-merge-conflict'.
if (error.code != 'firebaseui/anonymous-upgrade-merge-conflict') {
return Promise.resolve();
}
// The credential the user tried to sign in with.
var cred = error.credential;
// If using Firebase Realtime Database. The anonymous user data has to be
// copied to the non-anonymous user.
var app = firebase.app();
// Save anonymous user data first.
return app.database().ref('users/' + firebase.auth().currentUser.uid)
.once('value')
.then(function(snapshot) {
data = snapshot.val();
// This will trigger onAuthStateChanged listener which
// could trigger a redirect to another page.
// Ensure the upgrade flow is not interrupted by that callback
// and that this is given enough time to complete before
// redirection.
return firebase.auth().signInWithCredential(cred);
})
.then(function(user) {
// Original Anonymous Auth instance now has the new user.
return app.database().ref('users/' + user.uid).set(data);
})
.then(function() {
// Delete anonymnous user.
return anonymousUser.delete();
}).then(function() {
// Clear data in case a new user signs in, and the state change
// triggers.
data = null;
// FirebaseUI will reset and the UI cleared when this promise
// resolves.
// signInSuccessWithAuthResult will not run. Successful sign-in
// logic has to be run explicitly.
window.location.assign('<url-to-redirect-to-on-success>');
});
}

onDisconnect() fails with permission error on unauth()

I'm having trouble getting Firebase authentication to work with an anonymous user. I'm building a chat app using Firechat.js, which attaches a onDisconnect() handler to set the user "offline".
However, it seems when I call firebase.unauth() it logs the user out before the onDisconnect() has a chance to set the user "offline", so it fails with a permission error (this is my theory).
The log shows exactly how everything transpires as I login and then logout:
app.js: using username: John Smith <<logged in
app.js: calling firebase.unauth() <<logged out
firechat.js: Firechat Warning: Firechat requires an authenticated Firebase reference. Pass an authenticated reference before loading.
app.js: using username: Anonymous3aa-437b <<after logout, reauthenticate as anonymous
firebase.js: FIREBASE WARNING: set at /chat/user-names-online/john smith/-KE9LcpieTwxj_A4sBHz failed: permission_denied
As you see the user is anonymous before Firechat has a chance to set the previous user offline. Here is the onDisconnect() bit in firechat.js
Here's my app code:
var $firebase = new Firebase(...);
var $firechat = new Firechat(...);
$firebase.onAuth(function(authData) {
if (authData) {
var username = authData[authData.provider].displayName;
var anonusername = "Anonymous" + authData.uid.substr(10, 8);
console.log('using username: ' + username || anonusername);
//set firechat user and resume chatting session (user becomes "online")
$firechat.setUser(authData.uid, username || anonusername, function() {
$firechat.resumeSession();
});
} else {
//if not logged in, authenticate anonymously
$firebase.authAnonymously(function(error) {
if (error) {
console.log(error);
}
});
}
});
Here's the security rule for user-names-online:
"user-names-online": {
// A mapping of active, online lowercase usernames to sessions and user ids.
".read": true,
"$username": {
"$sessionId": {
".write": "(auth != null) && (!data.exists() || !newData.exists() || data.child('id').val() === auth.uid || auth.provider === 'anonymous')",
"id": {
".validate": "(newData.val() === auth.uid)"
},
"name": {
".validate": "(newData.isString())"
}
}
}
},
It seems firechat is built for this scenario, so why is it failing?
You seem to be confusing a few things:
calling unauth() drop the authentication session for the user.
It does not disconnect the user.
It does cancel any listeners for location that require authentication.
the code you attach to onDisconnect() runs on the Firebase servers, once they detect that the client has disconnected.
If the onDisconnect() handler removes the user from some sort of presence system, other clients can see that the user disappears.
the client that disconnected cannot immediately see the result of its own disconnect, because it has already disconnected.
if a client wants to know when it has disconnected, monitor the .info/connected value.

How do I use Firebase Simple Login with email & password

Firebase Simple login provides an email/password option, how do I use it? Starting from from creating a user, storing data for that user, to logging them in and out.
There are three distinct steps to be performed (let's assume you have jQuery):
1. Set up your callback
var ref = new Firebase("https://demo.firebaseio-demo.com");
var authClient = new FirebaseAuthClient(ref, function(error, user) {
if (error) {
alert(error);
return;
}
if (user) {
// User is already logged in.
doLogin(user);
} else {
// User is logged out.
showLoginBox();
}
});
2. User registration
function showLoginBox() {
...
// Do whatever DOM operations you need to show the login/registration box.
$("#registerButton").on("click", function() {
var email = $("#email").val();
var password = $("#password").val();
authClient.createUser(email, password, function(error, user) {
if (!error) {
doLogin(user);
} else {
alert(error);
}
});
});
}
3. User login
function showLoginBox() {
...
// Do whatever DOM operations you need to show the login/registration box.
$("#loginButton").on("click", function() {
authClient.login("password", {
email: $("#email").val(),
password: $("#password").val(),
rememberMe: $("#rememberCheckbox").val()
});
});
}
When the login completes successfully, the call you registered in step 1 will be called with the correct user object, at which point we call doLogin(user) which is a method you will have to implement.
The structure of the user data is very simple. It is an object containing the following properties:
email: Email address of the user
id: Unique numeric (auto-incrementing) ID for the user
FirebaseAuthClient will automatically authenticate your firebsae for you, not further action is required. You can now use something like the following in your security rules:
{
"rules": {
"users": {
"$userid": {
".read": "auth.uid == $userid",
".write": "auth.uid == $userid"
}
}
}
}
This means, if my User ID is 42, only I can write or read at example.firebaseio-demo.com/users/42 - when I am logged in - and no-one else.
Note that Simple Login does not store any additional information about the user other than their ID and email. If you want to store additional data about the user, you must do so yourself (probably in the success callback for createUser). You can store this data as you normally would store any data in Firebase - just be careful about who can read or write to this data!
Just incase someone is reached to this thread and looking for some example application using the firebase authentication. Here are two examples
var rootRef = new Firebase('https://docs-sandbox.firebaseio.com/web/uauth');
......
.....
....
http://jsfiddle.net/firebase/a221m6pb/embedded/result,js/
http://www.42id.com/articles/firebase-authentication-and-angular-js/

Resources