=encoding utf-8

=head1 NAME

Net::ACME - Client for the ACME protocol (e.g., Let���s Encrypt)

=head1 SYNOPSIS

    package MyACME::SomeService;

    sub _HOST { }   #return the name of the ACME host

    #See below for full examples.

=head1 DESCRIPTION

This module implements client logic for the ACME protocol,
the system for automated issuance of SSL certificates used by Let���s Encrypt.

The methods of this class return objects that correspond to the
respective ACME resource:

=over 4

=item * C<register()>: C<Net::ACME::Registration>

=item * C<start_domain_authz()>: C<Net::ACME::Authorization::Pending>

=item * C<get_certificate()>: C<Net::ACME::Certificate> or C<Net::ACME::Certificate::Pending>

=back

=head1 WHY USE THIS MODULE?

=over 4

=item * Closely based on cPanel���s widely used Let���s Encrypt plugin.

=item * Thorough error-checking: any deviation from what the ACME protocol
expects is reported immediately via an exception.

=item * Well-defined object system, including typed, queryable exceptions.

=item * Extensive test coverage.

=item * Light memory footprint - no Moose/Moo/etc.

=item * No careless overwriting of globals like C<$@>, C<$!>, and C<$?>.
(Hopefully your code isn���t susceptible to this anyway, but it���s just a good
precaution.)

=item * All dependencies are either core or pure Perl. For RSA crypto we use
L<Crypt::OpenSSL::RSA> if it���s available, or the system���s C<openssl> binary.
Net::ACME will run almost anywhere!

=back

=head1 STATUS

This module is now well-tested and should be safe for use in your application.

=head1 CUSTOMIZATION

B<OpenSSL>: This module will attempt to use L<Crypt::OpenSSL::RSA> if it is
available; if not, it falls back to the C<openssl> binary. On most systems this
should already be in the process���s search path, but if not, specify the binary���s
location by setting C<$Net::ACME::Crypt::OPENSSL_BIN_PATH>.

B<HTTPS options>: This module uses C<HTTP::Tiny> for its network operations.
In some instances it is desirable to specify custom C<SSL_options> in that
module���s constructor; to do this, populate
C<@Net::ACME::HTTP_Tiny::SSL_OPTIONS>.

=head1 URI vs. URL

This module uses ���uri��� for ACME-related objects and ���url��� for
HTTP-related ones. This apparent conflict is a result of maintaining
consistency with both the ACME specification (���uri���) and L<HTTP::Tiny> (���url���).

=head1 EXAMPLES

See the C<examples> directory in the distribution for complete, interactive
example scripts that also illustrate a bit of how ACME works.

See below for cut-paste-y examples.

=head1 EXAMPLE: REGISTRATION

    my $tos_url = Net::ACME::LetsEncrypt->get_terms_of_service();

    my $acme = Net::ACME::LetsEncrypt->new( key => $reg_rsa_pem );

    #Use this method any time you want to update contact information,
    #not just when you set up a new account.
    my $reg = $acme->register('mailto:who.am@i.tld', 'mailto:who@else.tld');

    $acme->accept_tos( $reg->uri(), $tos_url );

=head1 EXAMPLE: DOMAIN AUTHORIZATION & CERTIFICATE PROCUREMENT

    for my $domain (@domains) {
        my $authz_p = $acme->start_domain_authz($domain);

        for my $cmb_ar ( $authz_p->combinations() ) {

            #$cmb_ar is a set of challenges that the ACME server will
            #accept as proof of domain control. As of November 2016, these
            #sets all contain exactly one challenge each: ���http-01���, etc.

            #Each member of @$cmb_ar is an instance of
            #Net::ACME::Challenge::Pending--maybe a subclass thereof such as
            #Net::ACME::Challenge::Pending::http_01.

            #At this point, you examine $cmb_ar and determine if this
            #combination is one that you���re interested in. You might try
            #something like:
            #
            #   next if @$cmb_ar > 1;
            #   next if $cmb_ar->[0]->type() ne 'http-01';

            #Once you���ve examined $cmb_ar and set up the appropriate response(s),
            #it���s time to tell the ACME server to send its challenge query.
            $acme->do_challenge($_) for @$cmb_ar;

            while (1) {
                if ( $authz_p->is_time_to_poll() ) {
                    my $poll = $authz_p->poll();

                    last if $poll->status() eq 'valid';

                    if ( $poll->status() eq 'invalid' ) {
                        my @failed = grep { $_->error() } $poll->challenges();

                        warn $_->to_string() . $/ for @failed;

                        die "Failed authorization for ���$domain���!";
                    }

                }

                sleep 1;
            }
        }
    }

    #Make a key and CSR.
    #Creation of CSRs is well-documented so won���t be discussed here.

    my $cert = $acme->get_certificate($csr_pem);

    #This shouldn���t actually be necessary for Let���s Encrypt,
    #but the ACME protocol describes it.
    while ( !$cert->pem() ) {
        sleep 1;
        next if !$cert->is_time_to_poll();
        $cert = $cert->poll() || $cert;
    }

=head1 TODO

=over 4

=item * Find a way to sign with RSA in pure Perl. (NB: L<Crypt::RSA> has XS dependencies.)

=item * Support EC keys.

=item * Test and support more ACME v2 features (pending server support).

=back

=head1 THANKS

=over 4

=item * cPanel, Inc. for permission to adapt their ACME framework for
public consumption.

=item * Stephen Ludin for developing and maintaining C<Protocol::ACME>, from which
this module took its inspiration.

=back

=head1 SEE ALSO

I am aware of the following additional CPAN modules that implement this protocol:

=over 4

=item * L<Protocol::ACME>

=item * L<Crypt::LE>

=item * L<WWW::LetsEncrypt>

=item * L<Mojo::ACME>

=back

=head1 REPOSITORY (FEEDBACK/BUGS)

L<https://github.com/FGasper/p5-Net-ACME>

=head1 AUTHOR

Felipe Gasper (FELIPE)

=head1 LICENSE

This module is licensed under the same terms as Perl.