B4J Tutorial [Web][SithasoGunDB] - SEA: Security, Encryption & Authorization using IndexedDB Back-End

Hi Fam

Demo on Vercel

Today we look at how we can perform user authentication using GunDB. GunDB uses localstorage / indexedDb / file / and other databases as back-ends through various adapted.
The version that has been made available here uses IndexedDB as a back-end to store data from nodes.

One is able to have user authentication in GunDB, this means, we can add users, sign in and sign out and also be able to store data on user nodes in a secure way. This uses SEA (Security, Encryption and Authorization)

Introduction to GunDB


We will create a variety of screens.

1. Welcome
2. Register
3. Login
4. Forgot Password.

We will create our UI with SithasoIONIC7 Wireframes and SithasoIONIC7 for this example.

Here we go...

 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Welcome Screen

This provides access to Register or Login. Before this screen is show, the GunDB will be initialized in pgIndex, you can see this post below on how that is done.



This screen is just a gateway to access the register and login screens.

We have used an image from https://undraw.co/search

We then added 2 buttons with a divider "OR"

A user can register as a new user / login

B4X:
Private Sub btnregister_click (e As BANanoEvent)
    e.PreventDefault
    'navigate to the register page
    pgHome.show
End Sub
'
Private Sub btnlogin_click (e As BANanoEvent)
    e.PreventDefault
    'navigate to the login page
    pgLogin.show
End Sub
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Register

The register screen allows one to register their details. All the inputs are required and validated as soon as the user clicks Register. One is also able to click Back here to go to the Welcome Screen. One is able to toggle the password by clicking the append-button icon.



This page is the pgHome code module. When pgHome.Show is called when the register button is clicked. We clear the input controls and then clear their validation, ie. "Error help" text below the input components.

B4X:
'show this page
Sub Show
    'clear the inputs
    ioninput1.value = ""
    ioninput2.value = ""
    ioninput3.value = ""
    ioninput4.vALUe = ""
    'clear the validation
    ioninput1.ClearValidation
    ioninput2.ClearValidation
    ioninput3.ClearValidation
    ioninput4.ClearValidation
    'Show this page
    app.NavigateTo(path, "forward")
End Sub

When a user clicks "Register" on this screen, input validation takes place and then then if the details are specified, a user is created using the email address and password.

Let's look at the process flow.




B4X:
Private Sub ionbutton1_Click (e As BANanoEvent)
    e.PreventDefault
    ioninput1.ClearValidation
    ioninput2.ClearValidation
    ioninput3.ClearValidation
    ioninput4.ClearValidation
    app.ResetValidation
    app.Validate(ioninput1.IsBlank)
    app.Validate(ioninput2.IsBlank)
    app.Validate(ioninput3.IsBlank)
    app.Validate(ioninput4.IsBlank)
    If app.IsValid = False Then Return
    'initialize the user instance
    user.Initialize(Me, "gun", pgIndex.gun)
    'store the user details in session store, so that when tabs are changed, user will still be logged in
    user.SaveToSession
    'create the user using email and password
    user.Create(ioninput3.Value, ioninput4.Value)
 
End Sub

We have defined "user" as SithasoGunDB class.

A successful registration provides a public key for the user. As no passwords are stored, this public key can be used to store content in private for this user. Public keys, should be stored in a safe place as they provide access to the user profiles.



The gun_user_created method is called on success. We then use this method to update the user profile with the firstname, lastname and email address

B4X:
Sub gun_user_create (ack As Map)
    'read the pub key
    Dim pub As String = user.pub
    app.ShowToastSuccess("User", "Your profile has been created, please login")
    'save the user details to the user profile
    user.FirstName = ioninput1.Value
    user.LastName = ioninput2.Value
    user.Email = ioninput3.value
    'send public key to user's email
    'take the user to the login screen
    pgLogin.show
End Sub

Because we have used the email address as an "alias" / username for the account, one can get their "pub" key using the alias from the system, but not the password.
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Login

The login screen enables one to login into the system and also the forgot password functionality where one can enter their email address.

With GunDB, no passwords are stored however a security pair is kept, being the private and public keys.



When a user navigates to this page, the page details are cleared including validation. This page expects the email address and password used during registration.

B4X:
'show this page
Sub Show
    'clear the inputs
    ioninput7.Value = ""
    ioninput8.Value = ""
    'clear the validation
    ioninput7.ClearValidation
    ioninput8.ClearValidation
    'navigate to this page
    app.NavigateTo(path, "forward")
End Sub

Lets see the process flow.



If the user is successfully authorized, we have a successful login session, otherwise, an error is displayed.

We click the Login button, this reads the email and password and fires the auth method.
B4X:
Private Sub ionbutton3_Click (e As BANanoEvent)
    e.PreventDefault
    ioninput7.ClearValidation
    ioninput8.ClearValidation
    app.ResetValidation
    app.Validate(ioninput7.IsBlank)
    app.Validate(ioninput8.IsBlank)
    If app.IsValid = False Then Return
    Dim rec As Map = CreateMap()
    rec.put("email", ioninput7.value)
    rec.put("password", ioninput8.value)
    '
    'initialize the user instance
    user.Initialize(Me, "gun", pgIndex.gun)
    'store the user details in session store, so that when tabs are changed, user will still be logged in
    user.SaveToSession
    'authorize using email & password
    user.Auth(ioninput7.Value, ioninput8.Value)
End Sub

'on success, the auth method returns an encrypted gundb user.
B4X:
Sub gun_user_auth (ack As Map)
    Log(ack)
End Sub

This authorised user content will look like this..




'this is raised if the user cannot be authenticated

B4X:
'an error has been raised
Sub gun_user_Error (Error As String)
    app.ShowToastError("Auth", Error)
End Sub
 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Initializing our SithasoGunDB IndexedDB Back-End

We will do this in pgIndex.Initialize sub. You can do this on your BANano project entry point.

As we are not using NodeJS and we will use IndexedDB as a standalone, we will not add a peer to our project. The name of our IndexedDB in our browser will be "radata". Please note that this is NOT a BANanoSQL database, as GunDB saves and retrieves data from IndexedDB in its own way.

B4X:
'initialize gunDB for indexedDB
    gun.Initialize(Me, "gun")
    'set the db name
    gun.DbName = "radata"
    'use indexedDB
    gun.LocalStorage = False
    'use file on disk for NodeJS, set to true if you will install peer
    gun.Radisk = True
    'add a peer relay if you want
    'clone this repo: https://github.com/Mashiane/SithasoGunDBPeer01
    'install node.js, in terminal run 'npm install' then 'npn run start'
    'gun.AddPeer("http://localhost:8765")
    'connect to gun
    gun.connect

In your browser, press Control + Shift + I, go to Applications, expand indexedDB, see the database there.

 
Last edited:

Mashiane

Expert
Licensed User
Longtime User
Updates: Getting a list of Registered/Created Users

One can do this just after being authenticated, by calling user.LoadUsers. A user is added to the "Users" collection once their account is created. This list is stored in a public area i.e. accessible by everyone who has access to your db. It stores the alias and public key of the user.

B4X:
Sub gun_user_auth (ack As Map)
    app.ShowToastSuccess("Login", "You have a successful login!")
    'store the user details in session store, so that when tabs are changed, user will still be logged in
    user.SaveToSession
    'load available users
    user.LoadUsers
End Sub

This executes the callback

B4X:
Sub gun_users_load (Data As Map)
    Log(Data)
    user.Load
End Sub

B4X:
[LIST=1]
[*]{anelembanga: {…}, anelembanga1: {…}, usibabalembanga: {…}}
[LIST=1]
[*]anelembanga:
[LIST=1]
[*]alias: "anelembanga"
[*]pub: "9CMMECF5tA1k2t9LFVgzBuR8OZYzQ0elvTzOVRxtuSQ.bC6eg0-cnQ5QIVXp8vZ4I5PVg3v88JZ3TzumehKawjk"
[*][[Prototype]]: Object
[/LIST]
[*]anelembanga1:
[LIST=1]
[*]alias: "anelembanga1"
[*]pub: "N003wr2jBb4fs21yugL8LWA55zugYAAX3xIEXtulIew.QOGcliuzFaKim6ePTk5CnXSojiYTnCe-hWa6ff2ncWI"
[*][[Prototype]]: Object
[/LIST]
[*]usibabalembanga:
[LIST=1]
[*]alias: "usibabalembanga"
[*]pub: "qryjvYOn8_SRHXY6zWIReZXzqJG173tmxRAqZBnAoZU.PYNbtWiSEVCcCFqN3xJ3-nGWpV8gPgh-nBN9_7ui-TY"
[*][[Prototype]]: Object
[/LIST]
[*][[Prototype]]: Object
[/LIST]
[/LIST]


Reading our logged in account profile - No passwords are kept

In the above example, we also execute user.load, so that we can see our account profile. This will give us our details via the callback. The information in our account is only accessible to us and not the public. Others users who can write to it will be those ones with public key and are authorized to do so. We will look into that later.


B4X:
[LIST=1]
[*]{pub: '9CMMECF5tA1k2t9LFVgzBuR8OZYzQ0elvTzOVRxtuSQ.bC6eg0-cnQ5QIVXp8vZ4I5PVg3v88JZ3TzumehKawjk', alias: 'anelembanga', auth: '{"ek":{"ct":"ZwNAHUWEr4VqJ90DpMDJLdItxipl5Xs5e/SIL…TlnDF7ac5D3EjQNEz9fuDMHJFEyUrQjZ5z7tk0GuIMVCxFi"}', epub: 'P-Pzo_GiotFSV5SKOY-JD16KD3OOXA8upT8lRC4ItjQ.1PT0JbyR-2AzKISh5LczOpRhga-d-6cog-K2bGfvrFY', profile: {…}}
[LIST=1]
[*]alias: "anelembanga"
[*]auth: "{\"ek\":{\"ct\":\"ZwNAHUWEr4VqJ90DpMDJLdItxipl5Xs5e/SILMsGbD3wYX87GJJz2DZRAVixFeWtG2safqEf80pwldNuSEwqnoPczOPNBpTvEu11LKoBb9Qp4yOsL2QkQ9OXdk6OXsqKkbroGILSSAmq0YQgfve2l70tW3qWw3PNMJswOA==\",\"iv\":\"3QB3BNezMMirk/H7ZfNj\",\"s\":\"pjv7UiCzx5TB\"},\"s\":\"qmadSzTKS6XxmBI03TlnDF7ac5D3EjQNEz9fuDMHJFEyUrQjZ5z7tk0GuIMVCxFi\"}"
[*]epub: "P-Pzo_GiotFSV5SKOY-JD16KD3OOXA8upT8lRC4ItjQ.1PT0JbyR-2AzKISh5LczOpRhga-d-6cog-K2bGfvrFY"
[*]profile:
[LIST=1]
[*]email: "anele@mbangas.com"
[*]firstname: "Anele"
[*]lastname: "Mbanga"
[*][[Prototype]]: Object
[/LIST]
[*]pub: "9CMMECF5tA1k2t9LFVgzBuR8OZYzQ0elvTzOVRxtuSQ.bC6eg0-cnQ5QIVXp8vZ4I5PVg3v88JZ3TzumehKawjk"
[*]
[[Prototype]]: Object
[/LIST]
[/LIST]

This is returned by this callback

B4X:
Sub gun_user_load (Data As Map)
    'reading our logged in account
    Log(Data)
End Sub
 
Cookies are required to use this site. You must accept them to continue using the site. Learn more…