Subvert Firebase Authentication
orÂ
Use Firebase: One Authenticated User for Multi User Logins with App Inventor
INTRO
I am not saying you should do this, there are perhaps only a few use cases for doing this, but because it can be done I had a go at doing it.
When developing or testing with a Firebase Realtime Database project, you can start off with no security rules, but it very soon becomes clear that you need to have some security rules in place, to protect your users data, control who has access to your project, and to stop the pesky alerts from Firebase about security issues. It may be however, that you have a use case that really does not need all the bells and whistles provided by Firebase security and authentication, you could have an app you have built for family, friends or trusted users, or you want to have more control over who can do what from within your app, as opposed to the server side.
Why not, therefore, connect your app to your firebase project with just a single authenticated user, for all the users of your app? It is your app that is the authenticated user, not the person using the app. I did a google search for this which returned a Stackoverflow question on the very subject. The Firebase guru Doug Stevenson says it is possible to do this. If he says so, then why not.
I hope to show how you set up the initial authentication in Firebase, along with the security rules required, then how to create a signUp/SignIn system within the app, using your firebase, for all your users, and finally some methods for the storage and retrieval of data created by users, much along the lines of how you might set things up for normal authenticated users. The key, of course, is in keeping your single authenticated user details close to your chest. You will note that I am not using any of the built-in Firebase components or extensions available for Firebase, just the web component and the Firebase Web REST api. You can learn all about that here
SETUP Firebase Authentication
Firebase
You will need a firebase project with a realtime database. You can create a new one, or use an existing one. How to set one up is covered elsewhere.
From within the firebase console create a new bucket/node with a value of 0. I used FOAUMLU - an anacronym from the title above.
Now go to Authentication and set Email/Password to enabled - if not already.
Create a new user - I used foaumlu@mail.com and a 16 character password. Note the email address, password and the UID created
Now go into Rules and create a set of rules for your bucket/node (FOAUMLU), where <user uid> is the actual UID for your user that you noted down earlier.
{Â "rules":
{"FOAUMLU":
{".read": "auth.uid === '<user uid>'",
     ".write": "auth.uid === '<user uid>'"
  }
}
}These rules ensure that only your authenticating user can only access the bucket FOAUMLU, and only your authenticating user can read and write to it. The rules should look like this now:
{Â "rules":
{"FOAUMLU":
{".read": "auth.uid === 'Fg4xzpGK8yWtjhgac5wnk8gw8FGs1'",
     ".write": "auth.uid === 'Fg4xzpGK8yWtjhgac5wnk8gw8FGs1'"
  }
}
}Publish the rules
Go to your Project Settings and note the API key and the firebase url
For now, this is all that is needed to be done in Firebase
App Inventor App
Open up a new project, give it a suitable name.
Create variables for the email, password, API key, Firebase URL, UID, and projectBucket. Use the Obfuscated Text block for each of the entries.
(note: for your setup, you may wish to keep the password, or other items out of the app, only to be entered by a user once they are authorised)You will also need three more variables with an empty text: idToken, currentUser, and signType
In the designer, drag in a button, a notifier and a web component. ( I am going to be using several web components to help avoid confusion)
Change the button text to "Firebase Connect" and rename the button to "btConnect", and uncheck the Visible checkbox.
Name the web component "Web1FBAuth"Â
Now we will create two procedures to handle authentication:Â
The FirebaseConnect procedure can be added to a Screen1.initialise block, so that it runs on app start up
Drag out the button click event for the Firebase Connect button, and set the FirebaseConnect procedure to it.
Now we build the Web1FBAuth. GotText block
Here we test if the responseContent contains our user UID, if so, we store the idToken to our variable, and display an alert that we are connected.
The haJoin block, for a horizontal arrangement, is for user logins in the next stage (not essential for this first part to work)
If the connection fails, then we call the Connect procedure to try again, by presenting the Firebase Connect button
You can test this now in your companion. If all goes well, you should get the "Connected to Firebase" alert
That is it for the first part, your app is now connected by your single authenticating user foaumlu@mail.com, with security rules in place, to your Firebase realtime database, in the bucket FOAUMLU (only), with read and write capabilities. Any user of your app can use this (but, for the moment, there is nothing they can do!). Your app should be displaying a blank screen ;)
SETUP User SignUp/SignIn System (susi)
App Inventor App
This is the stage for setting up users in your app, once the app has connected to firebase. The entire susi system is setup and controlled in the app Inventor app. All user info is stored on and retrieved from Firebase. The only validation checks I have included is for duplicate usernames and an incorrect password. We will create a node below the projectBucket FAOUMLU called users, and each signed up user's details will be recorded within. No signed in user will be able to read the data in users, however the app will be able to, in order to susi a user.
In the designer, drag out a horizontal arrangement, and then place two buttons. Set them up like this:
In the blocks editor, drag out the button click events for each of the two blocks and set them up like this
When either of these buttons is clicked it sets the variable for signType and opens up the User Details arrangement
In the Designer, drag out a vertical arrangement, and load it with horizontal arrangements, labels, textboxes and a button like this:
Drag out a new web component and name this one Web2SignUpIn
Drag out a new web component and name it Web3StoreUsers
Now it will start to get a little more complicated. let us first look at the blocks for the Submit button:
We check if either of the two textboxes are empty, and if so, alert the user to enter the correct details
We hide the user details arrangement
We then test for the value of the signType variable. If the value is Up then we call the setSignUps procedure, and if not then call the getUsers procedure:
The actions for both appear to be the same, in terms of setting the Web2SignUpIn.url but the nodePath required is different.
For setSignUps, we are calling back all the data in users, for the getUsers we are calling back the password for the user seeking to sign in
All the magic happens in the Web2SignUpIn.GotText event:Â
We first test if the user is signing up or signing in
If signing up, then we test if their chosen username already exists, and if it does we re-direct them to the user details screen to try again
If their username is not already in the data, then we can proceed to add their details by calling the storeUsers procedure, then alert that they are signed up and signed in, and set the currentUser variable to their username, along with some tidying up.
User details are set up like this in firebase:
FOAUMLU
users
user1
password: "xyz"
username: "user1"
user2
password: "rst"
username: "user2"
If not signing up, and therefore signing in, we test if the password supplied is correct and if so confirm that the user is signed in, set the signed in user to the currentUser variable, and then do some tidying up.
(Note: blocks currently assumes user is capable of entering their username correctly!! - but this should fail at the getUsers stage and return incorrect responseContent)That is it for the user susi section. You now have an app that is connected to firebase with an authenticating user and a susi system to allow a user to sign up and sign in.
SETUP Storing and Retrieving Data
to be completed
First up, we need to decide how we want our app to work for users data, there are three main options:
all users can read and write all data
all users can read all data, but only write data to their own area
users can only read and write to their own area, their data is not shared with other users
I will try to show how these three options can be setup
Secondly, there are a variety of methods that can be used to store data
Firebase offers the POST, PUT and PATCH methods
we won't use POST, as this really only applies when there are many authenticated users
PATCH has only just become available in AI2, and is useful for changing just one element in a "Firebase" list without affecting the other elements
PUT is the method for creating new data, and is the most useful of the three, most examples will use PUT
We can use PUT to create a single node with a value, a "Firebase" list, a "Firebase" list with identified nodes, an "AI2" list
We can also Append to an "AI2" list, remove an item from an "AI2" list
Thirdly, for retrieving data, we simply use the node structure to identify the data we want returned at any level. we can also call back a "tagList" or "nodeList".
Let us first explore the web component url which will set how our users can store and retrieve data. This assumes we will set in stone any user requests in the blocks to a certain point in the data structure.
STORAGE
This structure would be used where all users can read and write to all data. The "base" node is /FOAUMLU/data and the user would be able to create the node animal with the value pig. Any use could overwrite this element
This structure would be used when a user, let us call them user1, can only write to their own area. The base node here is, for example: /FOAUMLU/data/user1. They can create the node animal with the value pig, but only they will be able to overwrite it. depending on the read data settings, other may be able to see it.
RETRIEVAL
Here we see how we would call back the data value for animal when all data is readable. If you wanted to return all the data, then simply remove the join / animal block
Here we see how to retrieve the data when only the user can access their own data
DATA SAVING METHODS
single node with a value
a "Firebase" list (the numbers: 0,1 etc. are automatically generated for you)
An "AI2" list
A "Firebase" list with identified nodes
A timestamp in milliseconds since epoch using the built-in timestamp feature in Firebase
you can replace a list like this...
with a list like this
As you will see, there are two more web components to add, one named Web4StoreData, the other Web5GetData.
The storeData procedure will look like this:
The getData procedure will look like this:
Both generate a responseContent of a JSON string/list which must be handled with the .JsonTextDecode block to covert to an "AI2" list or element
DOWNLOAD
This is setup to run for users to only have access to their own data. To create this with your own settings, you will need to follow the first part of this guide in order to provide:
authenticated user email
authenticated user password
authenticated user uid
firebase url
firebase API key
firebase projectBucket / starting node