Vio
Vio

Reputation: 83

How to integrate Google reCAPTCHA v2 for Blazor Webassembly with an ASP.NET Core?

I searched a lot of sources for the captcha integration, but didn't see how to integrate it into Blazor Webassembly. Please help me to have some resources available. Thanks very much.

(Most of my problem is that the .razor component cannot use the tag)

Upvotes: 2

Views: 7071

Answers (2)

Andrew Smith
Andrew Smith

Reputation: 31

The important thing is to call grecaptcha.render after the the Blazor component has rendered, i.e. call it from OnAfterRenderAsync.

Captcha.razor

@inject IJSRuntime JS

<span id="recaptcha" data-sitekey="@SiteKey"></span>

@code {

    [Parameter]
    public string SiteKey { get; set; }

    public async Task<string> GetResponse()
    {
        return await JS.InvokeAsync<string>("grecaptcha.getResponse");
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await JS.InvokeVoidAsync("grecaptcha.render", "recaptcha");
        }
    }

}

Using the above:

...
            <Captcha @ref="captchaComponent" SiteKey="@SiteKey"></Captcha>
...

@code
{
    Captcha captchaComponent;
...
        var reCaptchaResponse = await captchaComponent.GetResponse();
...

}

index.html

<script src="https://www.google.com/recaptcha/api.js"></script>
<script src="_framework/blazor.webassembly.js"></script>

A complete project that uses this is on GitHub here https://github.com/semata-com/SemataWebApp

Upvotes: 3

Fei Han
Fei Han

Reputation: 27803

I searched a lot of sources for the captcha integration, but didn't see how to integrate it into Blazor Webassembly.

To achieve the requirement of integrating the reCAPTCHA widget into Blazor Webassembly, you can try:

Create and include these two js files within wwwroot/Scripts folder

scriptLoader.js

// loadScript: returns a promise that completes when the script loads
window.loadScript = function (scriptPath) {
    // check list - if already loaded we can ignore
    if (loaded[scriptPath]) {
        console.log(scriptPath + " already loaded");
        // return 'empty' promise
        return new this.Promise(function (resolve, reject) {
            resolve();
        });
    }

    return new Promise(function (resolve, reject) {
        // create JS library script element
        var script = document.createElement("script");

        if (scriptPath === "https://www.google.com/recaptcha/api.js") {
            script.src = scriptPath + "?render=explicit";
            script.async = true;
            script.defer = true;
        }

        script.src = scriptPath;
        script.type = "text/javascript";

        console.log(scriptPath + " created");

        // flag as loading/loaded
        loaded[scriptPath] = true;

        // if the script returns okay, return resolve
        script.onload = function () {
            console.log(scriptPath + " loaded ok");
            resolve(scriptPath);
        };

        // if it fails, return reject
        script.onerror = function () {
            console.log(scriptPath + " load failed");
            reject(scriptPath);
        }

        // scripts will load at end of body
        document["body"].appendChild(script);
    });
}
// store list of what scripts we've loaded
loaded = [];

Note: the code referenced from this github example could help dynamically load JavaScript library, and I did some modification.

JsOfReCAPTCHA.js

function render_recaptcha(dotNetObj, selector, sitekey) { 
    return grecaptcha.render(selector, {
        'sitekey': sitekey,
        'callback': (response) => { dotNetObj.invokeMethodAsync('CallbackOnSuccess', response); },
        'expired-callback': () => { dotNetObj.invokeMethodAsync('CallbackOnExpired'); }
    });  
};

function getResponse(widgetId) {
    return grecaptcha.getResponse(widgetId);
}

Create and add a script reference in index.html

<script src="Scripts/scriptLoader.js"></script>

Create ReCAPTCHA.razor with following code

@page "/recaptcha"
@using  System.ComponentModel
@inject IJSRuntime JS;

<h3>reCAPTCHA</h3>


<div id="recaptcha_container"></div>

<button type="button" class="btn btn-primary" @onclick="ShowResponse">Show Response</button>

<br />

@reCAPTCHA_response

@code {
    private int WidgetId;

    public string reCAPTCHA_response;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        await JS.InvokeVoidAsync("loadScript", "https://www.google.com/recaptcha/api.js");

        await JS.InvokeVoidAsync("loadScript", "Scripts/JsOfReCAPTCHA.js");

        if (firstRender)
        {
            WidgetId = await JS.InvokeAsync<int>("render_recaptcha", DotNetObjectReference.Create(this), "recaptcha_container", "your_site_key_here");
        }

        await base.OnAfterRenderAsync(firstRender);
    }

    [JSInvokable, EditorBrowsable(EditorBrowsableState.Never)]
    public void CallbackOnSuccess(string response)
    {
        reCAPTCHA_response = response;
    }

    [JSInvokable, EditorBrowsable(EditorBrowsableState.Never)]
    public void CallbackOnExpired()
    {
        //...
    }


    private void ShowResponse()
    {
        reCAPTCHA_response = $"The response for the reCAPTCHA widget: {reCAPTCHA_response}";
    }
}

Test Result

enter image description here

Upvotes: 5

Related Questions