Captcha-CLOUDFLARE-Turnstile

 view release on metacpan or  search on metacpan

lib/Captcha/Cloudflare/Turnstile.pm  view on Meta::CPAN

    my $simple  = $self->scriptTag(%attr);
    return <<"EOL";
$simple
<div class="cf-turnstile" data-sitekey="$sitekey" data-action="$action"></div>
EOL
}

# verifiers =======================================================================
sub deny_by_score {
    my $self     = shift;
    my %attr     = @_;
    my $response = $attr{response} || croak "missing response token";
    carp "deny_by_score() is not applicable for Cloudflare Turnstile (no score); use verify() instead";
    return $self->verify($response);
}

sub verify_or_die {
    my $self     = shift;
    my %attr     = @_;
    my $response = $attr{response} || croak "missing response token";
    my $content  = $self->verify($response);
    return $content if $content->{success};
    die 'fail to verify Turnstile: ', $content->{'error-codes'}[0], "\n";
}

1;
__END__

=encoding utf-8

=head1 NAME

Captcha::Cloudflare::Turnstile - A Perl implementation for Cloudflare Turnstile

=head1 SYNOPSIS

 use Captcha::Cloudflare::Turnstile;
 my $ts = Captcha::Cloudflare::Turnstile->new(
     sitekey => '__YOUR_SITEKEY__', # Optional
     secret  => '__YOUR_SECRET__',  # Required
 );

 # In your HTML template, inside a <form> tag:
 print $ts->scripts( action => 'login' );
 # or separately:
 print $ts->scriptTag;
 print $ts->widgetTag( action => 'login' );

 # Server-side verification:
 my $content = $ts->verify($param{$ts});
 unless ( $content->{'success'} ) {
    die 'fail to verify Turnstile: ', @{ $content->{'error-codes'} }, "\n";
 }

=head1 DESCRIPTION

Captcha::Cloudflare::Turnstile is a subclass of L<Captcha::reCAPTCHA::V3> that implements
the L<Cloudflare Turnstile|https://www.cloudflare.com/products/turnstile/> CAPTCHA service.

It inherits C<verify()>, C<name()>, C<sitekey()>, and the utility methods from the base class,
and overrides the API endpoints and JavaScript helpers for Turnstile.

=head2 Basic Usage

=head3 new( secret => I<secret>, [ sitekey => I<sitekey>, query_name => I<query_name> ] )

Requires only secret when constructing.

You have to get them from L<Cloudflare Turnstile dashboard|https://dash.cloudflare.com/?to=/:account/turnstile>.

 my $ts = Captcha::Cloudflare::Turnstile->new(
    sitekey    => '__YOUR_SITEKEY__', # Optional
    secret     => '__YOUR_SECRET__',
    query_name => '__YOUR_QUERY_NAME__', # Optional, defaults to 'cf-turnstile-response'
 );

=head3 name([I<name>])

Get/set the form field name (I<query_name>). Defaults to C<'cf-turnstile-response'>.

 my $query_name = "$ts";   # stringification returns query_name

=head3 verify( I<response> )

Sends the token to the Cloudflare Turnstile siteverify endpoint and returns the decoded JSON response.

 my $content = $ts->verify($param{$ts});
 unless ( $content->{'success'} ) {
    die 'fail to verify Turnstile: ', @{ $content->{'error-codes'} }, "\n";
 }

=head3 verify_or_die( response => I<response> )

Calls C<verify()> and dies immediately on failure.

=head3 deny_by_score( response => I<response> )

Not applicable for Turnstile (no score is returned). Issues a warning and delegates to C<verify()>.

=head3 scriptURL()

Returns the Turnstile JavaScript API URL:
C<https://challenges.cloudflare.com/turnstile/v0/api.js>

No sitekey parameter is required in the URL for Turnstile.

=head3 scriptTag()

Returns C<< <script src="...api.js" defer></script> >>.

=head3 widgetTag( [ sitekey => I<sitekey>, action => I<action> ] )

Returns the Turnstile widget C<< <div> >> to place inside a C<< <form> >> tag.

 print $ts->widgetTag( action => 'login' );
 # <div class="cf-turnstile" data-sitekey="..." data-action="login"></div>

Turnstile automatically injects a hidden C<cf-turnstile-response> input into the form
when the challenge is completed.

=head3 scripts( [ sitekey => I<sitekey>, action => I<action> ] )

Returns C<scriptTag()> and C<widgetTag()> combined. Place this B<inside> the C<< <form> >> tag.

 print <<"EOL";
 <form action="./" method="POST">
    <input type="hidden" name="name" value="value">
    @{[ $ts->scripts( action => 'submit' ) ]}
    <button type="submit">send</button>
 </form>
 EOL

=head1 NOTES

To test this module strictly,
there is a necessary to run javascript in test environment.

Cloudflare provides dummy test keys for automated testing:

=over

=item Always passes: sitekey C<1x00000000000000000000AA>, secret C<1x0000000000000000000000000000000AA>

=item Always blocks: sitekey C<2x00000000000000000000AB>, secret C<2x0000000000000000000000000000000AA>



( run in 1.865 second using v1.01-cache-2.11-cpan-5c0b1e786e0 )