=encoding utf-8

=head1 NAME

Net::ACME - Client for the ACME protocol (e.g., L<Let���s Encrypt|http://letsencrypt.org>)

=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 (including SSL certificate issuance)
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 * Support for both RSA and ECDSA encryption (via L<Crypt::Perl>).

=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. Net::ACME will run
anywhere that Perl runs!

=back

=head1 STATUS

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

=head1 CUSTOMIZATION

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:me@example.com', 'mailto:who@example.com');

    $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 = map { $_->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 * Once the L<ACME specification|https://tools.ietf.org/html/draft-ietf-acme-acme>
is finalized, update this module to take advantage of the full specification.
As Let���s Encrypt���s L<Boulder|https://github.com/letsencrypt/boulder> is currently
the only widely-used ACME server, and that software is compatible with
L<the first draft of the ACME spec|https://tools.ietf.org/html/draft-ietf-acme-acme-01>,
there���s little reason to update for the time being.

=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 L<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.