Can't create a Firebase Auth Wrapper with WebControlSDK

  1. 3 months ago

    Leonidas B

    Jan 24 Macaé, RJ, Brazil
    Edited 3 months ago

    Hello, Firebase provides excellent authentication tools.

    I could follow these two tutorials and make simple websites to work:

    https://www.youtube.com/watch?v=hb85pYZSJaI

    https://www.youtube.com/watch?v=CvkCjfHts9A

    What I need to accomplish is to have a wrapper for it, so I can use it on my web apps.

    I tried following the webSDK manual to do that, but I'm not having success so far. I have already developed some wrappers myself, but I'm stuck at this one.

    Can you guys please give me some pointers?

    At present, the issue at hand is this annoying message on the web console:

    Error: Could not find the FirebaseUI widget element on the page. firebaseui.js:185:92

    I know it is there, I can see it on web inspector. Perhaps it can't be seen by Javascript because it is embedded in an iframe?

    Below is the code supplied by Firebase that searchs for the FirebaseUI widget, which I have put in the 'Firebase_Widget_Init' constant in my web app:

    <script src="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.js"></script>
    <link type="text/css" rel="stylesheet" href="https://cdn.firebase.com/libs/firebaseui/3.5.2/firebaseui.css" />
    <script type="text/javascript">
    	// FirebaseUI config.
    	var uiConfig = {
    		signInSuccessUrl: 'remote/index.php',
    		signInOptions: [
    		// Leave the lines as is for the providers you want to offer your users.
    		firebase.auth.GoogleAuthProvider.PROVIDER_ID,
    		//firebase.auth.FacebookAuthProvider.PROVIDER_ID,
    		//firebase.auth.TwitterAuthProvider.PROVIDER_ID,
    		//firebase.auth.GithubAuthProvider.PROVIDER_ID,
    		firebase.auth.EmailAuthProvider.PROVIDER_ID,
    		//firebase.auth.PhoneAuthProvider.PROVIDER_ID,
    		//firebaseui.auth.AnonymousAuthProvider.PROVIDER_ID
    		],
    		// tosUrl and privacyPolicyUrl accept either url string or a callback
    		// function.
    		// Terms of service url/callback.
    		tosUrl: '<your-tos-url>',
    		// Privacy policy url/callback.
    		privacyPolicyUrl: function() {
    		window.location.assign('<your-privacy-policy-url>');
    		}
    	};
    	// Initialize the FirebaseUI Widget using Firebase.
    	var ui = new firebaseui.auth.AuthUI(firebase.auth());
    	// The start method will wait until the DOM is loaded.
    	ui.start('#firebaseui-auth-container', uiConfig);
    </script>

    This code is called on the HTMLHeader Event of the WebControlWrapper.

    Public Shared Function HTMLHeader(CurrentSession as WebSession) as String
      If Not IsLibraryRegistered(CurrentSession, JavascriptNamespace, "lbmdataFireBaseUI") Then
        RegisterLibrary(CurrentSession, JavascriptNamespace, "lbmdataFireBaseUI")
        Dim sa() As String
        
        sa.Append Firebase_Initialization
        sa.Append Firebase_Widget_Init
        
        'Dim kp As String
        'kp = Join(sa, EndOfLine.UNIX)
        
        Return join(sa,EndOfLine.UNIX)
        
      End If
    End Function

    For some reason the web app is also failing to load the HTML, on the setter below:

    mHTML = value
    If ControlAvailableInBrowser() Then
    ExecuteJavaScript("Xojo.get('" + me.ControlID + "').innerHTML = '" + value + "';")
    End If

    Any ideas?

    @Greg OLone, thanks a lot for your input!

    You were right, ExecuteEvent wasn't being handled at all in the WebControlWrapper.

    I just set the code below and now it works as smooth as silk!

    Function ExecuteEvent(Name as String, Parameters() as Variant) Handles ExecuteEvent as Boolean
      Select Case Name
        
      Case "Logado"
        Logado(Parameters)
        
      Case "Deslogado"
        Deslogado(Parameters)
        
      End Select
      
    End Function
    

    I also simplified the Firebase_Action constant to:

    Public Const Firebase_Action as String = firebase.auth().onAuthStateChanged(function(user) {
        var res = [];
        var opcao = '';
        if (user) {
              // User is signed in.
              uid = user.uid;
              dname = user.displayName;
              res.push(uid);
              res.push(dname);
              opcao = 'Logado';
    	
        } else {
            uid = null;
            dname = null;
            var d = new Date();
            res.push("deslogado");
            res.push(d);
            opcao = 'Deslogado';
        }
        Xojo.triggerServerEvent('<<Controle>>', opcao, res);
          });

    After these changes, I got rid of the Webcontainercontrol and set the wrapper all by itself in a webpage... Just added a WebTextAre a for showing some output from the 'Logado' and 'Deslogado' events and a Webbutton to call Logout method.

    It just works!

  2. 2 months ago

    Leonidas B

    Feb 8 Macaé, RJ, Brazil

    Well, after a lot of struggling with it I finally managed to get this Firebase Auth thing working on Xojo... sort of. It feels clunky and awkward, or I'm just being too demanding of myself.

    In order for it to work I had to first understand that the login widget should not be handled by Xojo. There is an awful lot of javascript to it so it is best to let it sit on a standalone webpage just as shown on the youtube videos above (first post).

    As my need is to have an authentication method, I only have to deal with the second part, the "firebase.auth().onAuthStateChanged" part.

    This is how I did it:

    1 - Created a WebControlWrapper with the following objects:

    Constants:

    Public Const a_Logout as String = function logOut(){
    	firebase.auth().signOut();
    }
    Public Const Firebase_Action as String =     firebase.auth().onAuthStateChanged(function(user) {
            var res = [];
              var myuser = document.getElementById("user");
              var mynome = document.getElementById("nome");
    
            if (user) {
              // User is signed in.
              uid = user.uid;
              dname = user.displayName;
              myuser.innerHTML = uid;
              mynome.innerHTML = dname;
    
               document.cookie = "user=" + uid;
               document.cookie = "nome=" + dname;
    	
    	res.push(uid);
    	res.push(dname);
    
           Xojo.triggerServerEvent('<<Controle>>', 'Logado', res);
    	
            } else {
                uid = null;
                dname = null;
    
              myuser.innerHTML = "-";
              mynome.innerHTML = "-";
    
                document.cookie = "user=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
                document.cookie = "nome=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
    
    	var d = new Date();
    
    	res.push("deslogado");
    	res.push(d);
    
           Xojo.triggerServerEvent('<<Controle>>', 'Deslogado', res);
    
            }
          });
    
    Public Const Firebase_Init as String = <script src="https://www.gstatic.com/firebasejs/5.8.0/firebase.js"></script>
    
    Public Const Firebase_Main as String = // Initialize Firebase
    	var config = {
    		apiKey: "YOURFIREBASEAPI",
    		authDomain: "YOURFIREBASEDOMAIN",
    		databaseURL: "YOURFIREBASEDATABASEURL",
    		projectId: "YOURPROJECTID",
    		storageBucket: "YOURSTORAGEBUCKET",
    		messagingSenderId: "YOURMESSAGESENDERID"
    	};
        firebase.initializeApp(config);
    
    Public Const JavascriptNamespace as String = lbmdata_firebaseui

    Event Definitions:

    Event Deslogado(dado As Variant)
    Event Logado(dado As Variant)

    Event Handlers:

    Sub SetupCSS(ByRef Styles() as WebControlCSS) Handles SetupCSS
      Styles(0).Value("visibility") = "visible"
    End Sub
    
    Function SetupHTML() Handles SetupHTML as String
      Return "<div id=""" + self.ControlID + """>" + html + "</div>"
    End Function
    
    
    Sub Shown() Handles Shown
      Init
    
    End Sub
    

    Methods:

    Public Sub Init()
      Dim par As String
      
      par = ReplaceAll(Firebase_Action, "<<Controle>>", self.ControlID)
      par = par + EndOfLine.UNIX + a_Logout
      
      Self.HTML = par
    End Sub
    
    
    Public Sub Logout()
      Session.Cookies.Remove("user")
      Session.Cookies.Remove("nome")
      
      Self.HTML = "logOut();"
    End Sub
    

    Properties:

    Public Property HTML as String
    Get
      Return mHTML
    End Get
    
    Set
      mHTML = value
      If ControlAvailableInBrowser() Then
        ExecuteJavaScript(value)
        
      Else
        MsgBox("Firebase Widget not available in browser!")
        
      End If
    End Set
    
    End Property
    
    Private Property mHTML as String
    

    Shared Methods:

    Public Shared Function HTMLHeader(CurrentSession as WebSession) as String
      If Not IsLibraryRegistered(CurrentSession, JavascriptNamespace, "lbmdataFireBaseUI") Then
        RegisterLibrary(CurrentSession, JavascriptNamespace, "lbmdataFireBaseUI")
        
        Dim sa() As String
        
        sa.Append Firebase_Init
        sa.Append "<script>"
        sa.Append Firebase_Main
        sa.Append a_Logout
        sa.Append "</script>"
        
        Return join(sa,EndOfLine.UNIX)
        
      End If
    End Function
    

    In my first tests I used some HTML divs in a page, set as a WebPageSource, whose content is below:

    <div id="user">UID</div>
    <div id="nome">NOME</div>

    It worked, but I couldn't figure how to trigger the Xojo events called by Xojo.triggerServerEvent('<<Controle>>', 'Deslogado', res); and Xojo.triggerServerEvent('<<Controle>>', 'Logado', res);

    So... I embedded the WebControlWrapper (named as FireBase_APL) in a WebContainer along with a WebButton, a WebTimer, a WebTextArea. The button and the textarea area for developing and testing purposes only. The Timer is important for the overall result.

    These are the parameters added to the WebContainer:

    Event Definitions:

    Event Deslogou()
    Event Logou(nome As String, uid As String)

    Properties:

    Private Property logado as Boolean = False
    Private Property logadoold as Boolean = True

    The code in the Action event of the button is this:

    FireBase_APL.Logout

    And the code in the Timer.Action event is this:

    Dim dd As new Date
    Dim j() As String = Array(Session.Cookies.Value("user"), Session.Cookies.Value("nome"))
    
    txt_Feedback.AppendText Join(j) + " - " + dd.ShortTime + EndOfLine
    
    If j(0) = "" Then
    logado = False
    If Not logado And logadoold Then
    RaiseEvent Deslogou
    End If
    logadoold = False
    
    Else
    logado = True
    If logado And Not logadoold Then
    RaiseEvent Logou(j(0), j(1))
    End If
    logadoold = True
    
    End If

    This is how it works:

    The FirebaseUI Widget (not shown here) must have its "signInSuccessUrl" parameter set to your Xojo Web App URL, including the port, if it is a standalone deployment. On CGI deployment (my case), the URL must include the "app.cgi" part.

    When the web app receives focus from the browser, the javascript "firebase.auth().onAuthStateChanged" is fired. I have set it to create cookies for the user name and uid (and fire a Xojo triggerevent, that DOES NOT work!).

    The timer on the WebContainer checks whether these cookies are set or not and depending on the result, the Events "Logado" (portuguese for logged) and "Deslogado" are raised and treated at the webpage.

    I would really appreciate some help in making the Xojo.triggerServerEvents to work. That way I could get rid of the cookies, timer and WebContainer.

    For the time being, I consider this to be a working solution!

  3. Greg O

    Feb 8 Xojo Inc

    @LeonidasBrasileiro
    Did you try the quick start tutorial in the WebSDK pdf? It specifically shows how to use TriggerServerEvent.

    For instance... I don’t see here that you implemented the Event named ExecuteEvent which is where the event comes first, and then you are responsible for dispatching to your Event Definitions if necessary.

  4. Leonidas B

    Feb 9 Answer Macaé, RJ, Brazil

    @Greg OLone, thanks a lot for your input!

    You were right, ExecuteEvent wasn't being handled at all in the WebControlWrapper.

    I just set the code below and now it works as smooth as silk!

    Function ExecuteEvent(Name as String, Parameters() as Variant) Handles ExecuteEvent as Boolean
      Select Case Name
        
      Case "Logado"
        Logado(Parameters)
        
      Case "Deslogado"
        Deslogado(Parameters)
        
      End Select
      
    End Function
    

    I also simplified the Firebase_Action constant to:

    Public Const Firebase_Action as String = firebase.auth().onAuthStateChanged(function(user) {
        var res = [];
        var opcao = '';
        if (user) {
              // User is signed in.
              uid = user.uid;
              dname = user.displayName;
              res.push(uid);
              res.push(dname);
              opcao = 'Logado';
    	
        } else {
            uid = null;
            dname = null;
            var d = new Date();
            res.push("deslogado");
            res.push(d);
            opcao = 'Deslogado';
        }
        Xojo.triggerServerEvent('<<Controle>>', opcao, res);
          });

    After these changes, I got rid of the Webcontainercontrol and set the wrapper all by itself in a webpage... Just added a WebTextAre a for showing some output from the 'Logado' and 'Deslogado' events and a Webbutton to call Logout method.

    It just works!

or Sign Up to reply!