# Copyright (c) 2013 Mozilla.
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

package Net::BrowserID::Verify;
{
  $Net::BrowserID::Verify::VERSION = '0.003';
}
use Mouse; # use strict/warnings
use Carp;
use Exporter qw(import);

use LWP::Protocol::https;
use LWP::UserAgent;
use JSON::Any;
use HTTP::Request::Common qw(POST);

our @EXPORT_OK = qw(verify_remotely);
my $REMOTE_VERIFIER = 'https://verifier.login.persona.org/verify';

my $json = JSON::Any->new;

has type     => ( is => 'ro', isa => 'Str', default => 'remote' );
has audience => ( is => 'ro', isa => 'Str' );
has url      => ( is => 'ro', isa => 'Str', default => $REMOTE_VERIFIER );
has ua       => ( is => 'ro', builder => 'make_ua' );

sub make_ua {
    my $ua = LWP::UserAgent->new();
    $ua->ssl_opts( verify_hostname => 1 );
    return $ua;
}

sub verify {
    my ($self, $assertion) = @_;

    my $req = POST $self->url, [ audience => $self->audience, assertion => $assertion ];
    my $resp = $self->ua->request($req);

    my $data;

    if ($resp->is_success) {
        my $message = $resp->decoded_content;
        $data = $json->decode($message);
    }
    else {
        $data = {
            status => 'failure',
            reason => $resp->message,
        };
    }

    return $data;

}

sub verify_remotely {
    my ($assertion, $audience, $opts) = @_;

    my $verifier = Net::BrowserID::Verify->new({
        type     => q{remote},
        audience => $audience,
        url      => $opts->{url} || $REMOTE_VERIFIER,
    });

    return $verifier->verify('assertion');
}

1;

__END__

=pod

=head1 NAME

Net::BrowserID::Verify - Verify BrowserID assertions.

=head1 VERSION

version 0.003

=head1 SYNOPSIS

  # Procedural API
  use Net::BrowserID::Verify qw(verify_remotely);
  my $data = verify_remotely('assertion', 'audience');

  # OO API
  use Net::BrowserID::Verify;
  my $verifier = Net::BrowserID::Verify->new({
      type     => q{remote},
      audience => q{http://localhost},
  });

  my $data = $verifier->verify('assertion');

=head1 EXPORTS

The following functions can be exported from the C<Net::BrowserID::Verify> module.
No functions are exported by default.

=head2 verify_remotely(assertion, audience, opts)

Returns the result of either a verified assertion, a failed assertion or a failed
request. See below for which fields are contained in the returned data.

The following params are required:

=over 4

=item assertion

This is the assertion you receive in the browser from the C<onlogin> callback which
you should post to your server for verification. It is an opaque value which you
should not change.

=item audience

This is your website, essentially C<http://example.com>. This is required by
the verifier to make sure the assertion is for your site.

=back

The following names options can be passed as an opts hash:

=over 4

=item url

This is the URL that you would prefer to use when using a remote verifier. It has
the default 'https://verifier.login.persona.org/verify'.

=back

=head1 RETURNED DATA

Once you have $data from the verifier function of your choosing, you can then
check if the status was okay.

  if ( $data->{status} eq 'okay' ) {
      # read $data->{email} to set up/login your user
      print $data->{email};
  }
  else {
      # something went wrong with the verification or the request
      print $data->{reason};
  }

=head2 Fields

The assertion format you receive when using Persona/BrowserID needs to be
sent from your browser to the server and verified there. This library
helps you verify that the assertion is correct.

The data returned by C<verify_remotely()>, (eventually) C<verify_locally()> or
C<$verifier-E<gt>verify()> contains the following fields:

=over 4

=item status

The status of the verification. Either 'okay' or 'failure'.

=item email

The email address which has been verified.

Provided only when status is 'okay'.

=item issuer

The issuer/identity provider, which should be either the domain of the
email address being verified, or the fallback IdP.

Provided only when status is 'okay'.

=item expires

The expiry (in ms from epoch). e.g. 1354217396705.

Provided only when status is 'okay'.

=item audience

The audience you passed to the verifier.

Provided only when status is 'okay'.

=item reason

Gives the reason why something went wrong.

Only provided if the status is 'failure'.

=back

=head1 AUTHOR

Andrew Chilton "chilts@mozilla.com"

=head1 COPYRIGHT AND LICENSE

Copyright (c) 2013 Mozilla.

This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.

=cut