Firebase Demo: 

Secured 

with Web Component

INTRO

You should now have a good understanding of the basic mechanics of running Firebase through a Web component, and of the need for using Firebase Security on your data and file storage. Here we will put these two together, again just using the Web component, and provided authenticated access.

Firebase offers some 12 different authentication solutions, I will be using the simplest and most straight forward "email/password" option, easy to use, easy to configure, easy to understand. You can see the various options on the Sign in method page under Authentication, and you will need to Enable a method in order to use it. Each user is provided with a "User UID" or "localId" a string of letters and numbers that can be used to identify the user within Firebase data. When a user logs in (signs in), Firebase returns a token that can be used to authenticate their presence in the Firebase project, and what they can and cannot do is dictated by the rules that are set.

As a developer, you are going to need the API key, the Firebase URL, and the Storage Application ID. These can all be found in the project settings for the database and storage. You should also set up a tag/ProjectBucket for the database and a folder for Storage. In both instances I will use FBDEMO for this. For the purposes of the demo I will refer throughout as follows:

If I show any user tokens, they will probably be as they are, or shortened for viewing purposes, they will have expired by time of going to print!

For the realtime database, I will use the rules that only allow an authenticated user to write to and read data in their own area, and set just for FBDEMO:

{

  "rules": {

    "FBDEMO": {

       "$uid": {

         ".read": "$uid === auth.uid",

         ".write": "$uid === auth.uid"

       }

     }

   }

}

For the firebase storage, I will use rules that allow authenticated users to read and write everything, within the FBDEMO folder:

rules_version = '2';

service firebase.storage {

  match /b/myProject.appspot.com/o {

    match /FBDEMO/{allPaths=**} {

      allow read: if request.auth != null

      allow write:  if request.auth != null;

    }

  }

}


Firebase Realtime Database with Secure Rules

SIGN UP a New User

If you have watched some of Taozilla's videos (See resources on the start page) and reviewed the documentation, you will have seen that there is a wide range of "things" you can do with a new user, verifying their email, setting their profile details. For our purposes here, we will just sign up a user.

Url:

https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=<APIKey>

PostText:

email=<emailAddress>&password=<password-Min.6>&returnSecureToken=true

Example:

https://identitytoolkit.googleapis.com/v1/accounts:signUp?key=AIzaShC3m_gRuFjgH61xBV2S4ZKJL0m1pTpWRM2

email=joe@gmail.com&password=abc123&returnSecureToken=true

Returns: (note "idToken" and "refreshToken" have been shortened)

{

  "kind": "identitytoolkit#SignupNewUserResponse",

  "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1...p0olCMoOcyG6Zan2WtSOe8H-2hfw",

  "email": "joe@gmail.com",

  "refreshToken": "AG8BCnc4UUY_-jgOarPiz8PaxgY_CbG5UK3EEx2X...6C8KMxM33xlZK3YA",

  "expiresIn": "3600",

  "localId": "uBzLUCxxP9VOuahNseg878JdB6E3"

}

We will need to store the "idToken" and "localId" (uid) to variables for the session. 

For auto renew of the idToken we could use the "refreshToken" and the "expiresIn" time in a routine.

The user is now signed up, and also signed in, they could start work. But we also need to see what happens if they sign in...

SIGN IN (LOGIN) a User

Much the same details are required for a sign in, a slightly different url, and different information in the returns:

Url:

https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=APIKey

PostText:

email=<emailAddress>&password=<password-Min.6>&returnSecureToken=true

Example:

https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=AIzaShC3m_gRuFjgH61xBV2S4ZKJL0m1pTpWRM2

email=joe@gmail.com&password=abc123&returnSecureToken=true

Returns: (note "idToken" and "refreshToken" have been shortened)

{

  "kind": "identitytoolkit#VerifyPasswordResponse",

  "localId": "uBzLUCxxP9VOuahNseg878JdB6E3",

  "email": "joe@gmail.com",

  "displayName": "",

  "idToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4Zjhi...IhawGQXLn-gOlVJQ4KxenovzOSUV_wmA",

  "registered": true,

  "refreshToken": "AG8BCndcseQhtIH2mOXQ-55vqu0wJl6y1...P4D6o515CMNt8pRE",

  "expiresIn": "3600"

}

Note the "registered":true option

REFRESH idToken for user

The idToken for a user, once signed in, will last for an hour, then it will need refreshing. You use the refreshToken shown in the signin return above to do this.

Url:

https://securetoken.googleapis.com/v1/token?key=APIKey

PostText:

grant_type=refresh_token&refresh_token=theRefreshToken

Example:

https://securetoken.googleapis.com/v1/token?key=AIzaShC3m_gRuFjgH61xBV2S4ZKJL0m1pTpWRM2

grant_type=refresh_token&refresh_token=AG8BCndcseQhtIH2mOXQ-55vqu0wJl6y1...P4D6o515CMNt8pRE

Returns: (Note "access_token","id_token" and "refresh_token" have been shortened. Note also that the access_token == the id_token)

{

  "access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc...tRDUzId0_Wgjm3MW6-AkZ1j38a1zXg",

  "expires_in": "3600",

  "token_type": "Bearer",

  "refresh_token": "AMf-vBwPNHd1ZK324sqh7zO59hJlK...GiYBVUYi0TPqgjzT4LAb74QnvYTgYO7NsmR44",

  "id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6Ijc...tRDUzId0_Wgjm3MW6-AkZ1j38a1zXg",

  "user_id": "2HQIFCAifCVDAy5Ogu8VeNhwUPk1",

  "project_id": "679881131398"

}

Use the new idToken for access to the firebase data

PUT

Ok, let us get Joe to PUT some data, for example, his age...

Url:

https://<firebaseUrl>/<projectBucket>/<uid>.json?auth=<idToken>

PutText:

{"tag":data}

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/Dl7oRoAJwwSyfaXXgSnfII0qZ3y2.json?auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1NWUyYjM1 

{"age":46}

Returns: {"age":46}

PATCH

This allows the user to "add" items to their firebase list under the uid without removing the existing data!

OK, let us get Joe to PATCH some data, for example, his height in inches...

Url:

https://<firebaseUrl>/<projectBucket>/<uid>.json?auth=<idToken>

PutText:

{"tag":data}

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/RxmYV2p...raeHTNwH2.json?auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImFl...ZAcLIt7qcGqtNhA 

{"height":67}

Returns: {"height":67}

POST

Now Joe will POST some data, for example, a message...

Url:

https://<firebaseUrl>/<projectBucket>/<uid>.json?auth=<idToken>

PostText:

data

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/Dl7oRoAJwwSyfaXXgSnfII0qZ3y2.json?auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1NWUyYjM1 

Hello, how are you?

Returns: {"name":"-MKfgL5wu3BzefpnA8CX"}

GET

Now Joe will GET some data...

Url:

https://<firebaseUrl>/<projectBucket>/<uid>/<tag>.json?auth=<idToken>

Get:

data

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/Dl7oRoAJwwSyfaXXgSnfII0qZ3y2/age.json?auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1NWUyYjM1 

Returns: 46

DELETE

Now Joe will DELETE a tag and its data

Url:

https://<firebaseUrl>/<projectBucket>/<uid>/<tag>.json?auth=<idToken>

Delete:

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/Dl7oRoAJwwSyfaXXgSnfII0qZ3y2/age.json?auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1NWUyYjM1 

Returns: null

GET (TAGS)

You can GET TAGS using the "?shallow=true" parameter - just like in the no security example

Url:

https://<firebaseUrl>/<projectBucket>/<uid>/<tag>.json?shallow=true&auth=<idToken>

Get:

data

Example:  (idToken shortened)

https://myProject.firebaseio.com/FBDEMO/Dl7oRoAJwwSyfaXXgSnfII0qZ3y2.json?shallow=true&auth=eyJhbGciOiJSUzI1NiIsImtpZCI6ImQxMGM4ZjhiMGRjN2Y1NWUyYjM1 

Returns: {"-MKflx2zqr6UKd_WIroh":true,"-MKflxY6PSR1rvfDMfub":true,"-MKflxpUp0sVf71CQLeF":true}

Firebase Storage with Secure Rules

UPLOAD A FILE

In order for this to work, you need to have captured the secureToken (idToken) when an authenticated user signed in (logged in). This token is then used in the web headers as an Authorization / Bearer [token]. If the secureToken is not valid, then the file upload will fail. Also ensure that the mimetype is set correctly.

Header:

content-type : <mimetype>

Authorization : Bearer <idToken>

Url:

https://firebasestorage.googleapis.com/v0/b/<applicationID>/o/<folder>%2F<filename>?alt=media

PostFile:

<full/path/to/filename>


Example: (idToken is shortened)

[

["content-type", "image/png"],

 ["Authorization", "Bearer eyJhbGciOiJSUzI1...YacwA"]

]

https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMO%2Fmyimage1.png?alt=media

/storage/emulated/0/Android/data/edu.mit.appinventor.aicompanion3/files/myimage1.png

Note: (something changed ?) the above blocks are showing an absolute path to the file for PostFile. It may be that you need a full path, e.g to include "file://" before the rest of the path shown if you get an error 1104 showing up.


Returns:

{

  "name": "FBDEMO/myimage1.png",

  "bucket": "myProject.appspot.com",

  "generation": "1603884334214831",

  "metageneration": "1",

  "contentType": "image/png",

  "timeCreated": "2020-10-28T11:25:34.214Z",

  "updated": "2020-10-28T11:25:34.214Z",

  "storageClass": "STANDARD",

  "size": "38106",

  "md5Hash": "ZGOY4M+44ZmWCTAHLvtCtQ==",

  "contentEncoding": "identity",

  "contentDisposition": "inline; filename*=utf-8''myimage1.png",

  "crc32c": "AlvaFg==",

  "etag": "CK/dgeCW1+wCEAE=",

  "downloadTokens": "05e71cdd-5b56-442a-ab79-9e07e64ecdc6"

}


The developer needs to capture and store the "name" and the "downloadTokens" for accessing the file

DOWNLOAD A FILE

(get/create the download url to access the file)

There are two options here, and this depends on how you want to treat the uploaded file. If you want to keep the file inside the secure "bubble", and therefore only accessible to authenticated users, then the user will need to employ their secureToken for Authorization again, and build a url WITHOUT the "downloadTokens".  In AppInventor, this may mean the only way to access the file is to actually download it to your device using the Web component. Alternatively, we can build a url that includes the "downloadTokens", which makes the file world readable, although of course you have to have the url and the "downloadTokens" to be able to access the file. In App Inventor this relaxes the need to download the file, for an image, for example, you can set the image.Picture to the url.

In either case, the url required is very similar to the one used to upload....

Header:

content-type : <mimetype>

Authorization : Bearer <idToken>

Secure Url:

https://firebasestorage.googleapis.com/v0/b/<applicationID>/o/<folder>%2F<filename>?alt=media

Example:

https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMO%2Fmyimage1.png?alt=media

---------------------------------------------------------------------------------------------

World Readable Url:

https://firebasestorage.googleapis.com/v0/b/<applicationID>/o/<folder>%2F<filename>?alt=media&token=<downloadTokens>

Example:

https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMO%2Fmyimage1.png?alt=media&token=05e71cdd-5b56-442a-ab79-9e07e64ecdc6

World readable - will display image in image component

Secure Download - will download file to device

DELETE A FILE

Very similar to the Secure Download, just replace Web1.Get with Web1.Delete, and remove the File Save setting blocks

Url:

https://firebasestorage.googleapis.com/v0/b/<applicationID>/o/<folder>%2F<filename>?alt=media

Delete:


Example:

https://firebasestorage.googleapis.com/v0/b/myProject.appspot.com/o/FBDEMO%2Fmyimage1.png?alt=media

Returns: nothing is returned on delete

In Summary:

I made a video of the process for the no rules version on the previous page, if you need to see the process in action (the secure rules method looks the same on screen)


I guess it is worth a warning that Google like to change things without notice or advice, therefore parts or all of this may stop working, or it all gets completely replaced with something else (hopefully a bit easier to handle). I have seen that "identityToolkit" is on its way out, this will probably be replaced with something "firebasey"....


If you are following the methods you have seen in this guide, then take your time, be precise, test and test again to ensure you have everything right, and you will have yourself an App Inventor app that can access Firebase Realtime database and Firebase Storage, with secure rules, without having to use the built-in experimental firebase component, or to need to use any of the extensions provided to "make this easier"......

Thanks for watching :)