Search code examples
javascriptcallbackfacebook-javascript-sdkblazor-client-side

How to get facebook login token in Blazor Client properly?


The application is a Blazor Client and try to implement facebook authentication.

Following the facebook guidelines I have the following code:

JS part:

window.fbAsyncInit = function () {
    FB.init({
        appId: '...',
        autoLogAppEvents: true,
        xfbml: true,
        version: 'v7.0'
    });

    // To don't call the FB.login directly
    function login() {
        FB.login(function (response) {
            if (response.authResponse) {
                console.log('Welcome!  Fetching your information.... ');
                var accessToken = response.authResponse.accessToken;
                console.log(accessToken.getToken());
                //location.href = "REDIRECT_TO.php";
                //await callbackMethod.invokeMethodAsync('Callback', accessToken);
            }
            else {
                console.log('User cancelled login or did not fully authorize.');
                //await callbackMethod.invokeMethodAsync('Callback', 'xx');
            }
        });

        FB.getLoginStatus(function (response) {
            if (response.status === 'connected') {
                var accessToken = response.authResponse.accessToken;
                return accessToken;
            }
        });
    }
};

C# part:

public async Task FBLogin()
{
    var reference = DotNetObjectReference.Create(this);
    await jsRuntime.InvokeVoidAsync("FB.login", reference);
}

[JSInvokable("Callback")]
public void Callback(object token)
{
    // things to do..
}

Question 1. Possible to retrieve directly any value synchronously from js to C#? (there is only async access to the js)

Question 2. Using a callback function is the proper way to retrieve the token? Where to place "callbackMethod" as an argument? (I am not a js pro, I tried everything and none of them worked).

Without passing the reference the login is working well. When I pass the following error message shows up: enter image description here


Solution

  • First create a javascript file in "wwwroot/js/" in your blazor webassembly project and name it "fbInit.js", put this code inside the file:

    window.fbAsyncInit = function () {
        window.FB.init({
            appId: 'your-fb-app-id',
            cookie: true,
            xfbml: true,
            version: 'v7.0'
        });
    
        window.FB.AppEvents.logPageView();
    };
    
    (function (d, s, id) {
        var js, fjs = d.getElementsByTagName(s)[0];
        if (d.getElementById(id)) { return; }
        js = d.createElement(s); js.id = id;
        js.src = "https://connect.facebook.net/en_US/sdk.js";
        fjs.parentNode.insertBefore(js, fjs);
    }(document, 'script', 'facebook-jssdk'));
    

    Now create this other javascript file in "wwwroot/js" and name it, "fbLogin.js", put this code inside the file:

    window.fbLogin = function() {   
        window.FB.login(function (response)
        {
            if (response.status === 'connected') {
                //console.log(response)            
            } else {
                console.log("No se logeo");
            }
    
            DotNet.invokeMethodAsync('WebClient-Blazor', 'FbLoginProcessCallback', response);
            
        }, { scope: 'public_profile, email' });       
    }
    

    Now you need to declare the existence of those scripts your "wwwroot/index.html" file of your webassembly project, do it like this:

    <script src="_framework/blazor.webassembly.js"></script>
    <!--put it below blazor.webassembly.js declaration -->
    <script src="js/fbInit.js"></script>
    <script src="js/fbLogin.js"></script>
    

    Now inside your "shared/MainLayout.razor" file, insert this code:

    protected override async Task OnInitializedAsync()
    {
        base.OnInitialized();
    
        await JSRuntime.InvokeVoidAsync("fbAsyncInit");
    }
    

    Remember to Inject IJSRuntime interface before start your code part of the razor file, just put this dependency injection insertion:

    @inject IJSRuntime jsRuntime

    @code
    {
    //all your razor code 
    }
    

    Finally, inside your login component or page where your have the Button that will perform Facebook Login, create this async method and call it from @onclick method of your custom facebook button:

    public async Task FbLoginProcess()
    {
       await JSRuntime.InvokeAsync<object("fbLogin");
    }
    

    Add this other method if you want to call back your c# code from javascript code and pass facebook login result.

    [JSInvokable("FbLoginProcessCallback")]
    public static void FbLoginProcessCallback(object result)
    {
        Console.WriteLine(result);
    }
    

    The javascript function "fbLogin" will return the Facebook Response object to your C# "FbLoginProcessCallback" function, you can use it like you want, maybe to make a httpclient post request to your own API or to use Blazor NavigationManager to redirect to another page or just to save it in your local storage the object.

    Hope this answer helps you.

    For more information about JSInterop, click here!