Experian-IDAuth

 view release on metacpan or  search on metacpan

lib/Experian/IDAuth.pm  view on Meta::CPAN

package Experian::IDAuth;

use 5.010;
use strict;
use warnings;

our $VERSION = '2.53';

use Locale::Country;
use Path::Tiny;
use Syntax::Keyword::Try;
use WWW::Mechanize;
## no critic (DiscouragedModules)
use XML::Simple;
use XML::Twig;
use SOAP::Lite;
use File::MimeInfo::Magic;
use IO::Socket::SSL 'SSL_VERIFY_NONE';
use Carp;
use Digest::SHA qw(hmac_sha256_base64);

sub new {
    my ($class, %args) = @_;
    my $obj = bless {}, $class;
    $obj->set($obj->defaults, %args);
    return $obj;
}

sub defaults {
    my $self = shift;
    return (
        username      => 'experian_user',
        password      => '?',
        private_key   => 'private_key',
        public_key    => 'public_key',
        members_url   => 'https://proveid.experian.com',
        api_uri       => 'http://corpwsdl.oneninetwo',
        api_proxy     => 'https://xml.proveid.experian.com/IDSearch.cfc',
        header_ns_url => 'http://xml.proveid.experian.com/xsd/Headers',
        folder        => '/tmp/proveid',

        # if you're using a logger,
        #logger     => Log::Log4per::get_logger,
    );
}

sub set {
    my ($self, %args) = @_;
    $self->{$_} = $args{$_} for keys %args;
    return $self;
}

sub get_result {
    my $self = shift;

    my $search_option = $self->{search_option};

    croak "invalid search_option $search_option" if $search_option !~ /(?:ProveID_KYC|CheckID)/;

    $self->_do_192_authentication;

    return $self->_get_result_proveid if $search_option eq 'ProveID_KYC';

    return $self->_get_result_checkid;
}

sub save_pdf_result {
    my $self = shift;

    # Parse and convert the result to hash
    my $result = $self->_xml_as_hash or croak 'no xml result in place';

    # 192 reference which we should pass to 192 to download the result
    my $our_ref = $result->{OurReference} or croak "No 'OurReference'; invalid save-pdf request";

    my $url  = $self->{members_url};
    my $mech = WWW::Mechanize->new();
    $mech->ssl_opts(
        verify_hostname => 0,
        SSL_verify_mode => SSL_VERIFY_NONE,
        SSL_key_file    => "/etc/rmg/ssl/key/experian.key",
        SSL_cert_file   => "/etc/rmg/ssl/crt/experian.crt"
    );

    try {

        # Get the login page
        $mech->get("$url/signin/");

        # Login to the members environments
        $mech->submit_form(
            with_fields => {
                login    => $self->{username},
                password => $self->{password},

lib/Experian/IDAuth.pm  view on Meta::CPAN


sub has_downloaded_pdf {
    my $self     = shift;
    my $file_pdf = $self->_pdf_report_filename;
    return 0 unless -e $file_pdf;
    open my $fh, '<', $file_pdf or croak "Could not open $file_pdf: $!";
    my $mime_type = mimetype($fh);
    close $fh;
    return $mime_type =~ /PDF/i;
}

sub has_done_request {
    my $self = shift;
    return -f $self->_xml_report_filename;
}

sub get_192_xml_report {
    my $self = shift;
    return Path::Tiny::path($self->_xml_report_filename)->slurp;
}

sub valid_country {
    my $self    = shift;
    my $country = shift;
    for (

        # To make CheckID work well for non-UK countries we need to pass
        # in drivers license, Passport MRZ, national ID number
        #qw( ad at au be ca ch cz dk es fi fr gb gg hu ie im it je lu nl no pt se sk us )
        qw ( gb im )
        )
    {
        return 1 if $country eq $_;
    }
    return 0;
}

sub _build_request {
    my $self = shift;

    $self->{request_as_xml} =
          '<xml><![CDATA[<Search>'
        . $self->_build_authentication_tag
        . $self->_build_country_code_tag
        . $self->_build_person_tag
        . $self->_build_addresses_tag
        . $self->_build_telephones_tag
        . $self->_build_search_reference_tag
        . $self->_build_search_option_tag
        . '</Search>]]></xml>';

    return 1;
}

# This is built based Section 3b on the Experian User Guide
sub _2fa_header {
    my $self = shift;

    my $loginid     = $self->{username};
    my $password    = $self->{password};
    my $private_key = $self->{private_key};
    my $public_key  = $self->{public_key};

    my $timestamp = time();

    my $hash = hmac_sha256_base64($loginid, $password, $timestamp, $private_key);

    # Digest::SHA doesn't pad it's outputs so we have to do it manually.
    while (length($hash) % 4) {
        $hash .= '=';
    }

    my $hmac_sig = $hash . '_' . $timestamp . '_' . $public_key;

    return SOAP::Header->name('head:Signature')->value($hmac_sig);
}

# Send the given SOAP request to 192.com
sub _send_request {
    my $self = shift;

    my $request = $self->{request_as_xml} // croak 'needs request';

    # Hide password
    (my $request1 = $request) =~ s/\<Password\>.+\<\/Password\>/\<Password\>XXXXXXX<\/Password\>/;

    # Create soap object
    my $soap = SOAP::Lite->readable(1)->uri($self->{api_uri})->proxy($self->{api_proxy})->ns($self->{header_ns_url}, 'head');

    $soap->transport->ssl_opts(
        verify_hostname => 0,
        SSL_verify_mode => SSL_VERIFY_NONE
    );
    $soap->transport->timeout(60);

    # Do it
    my $som = $soap->search(SOAP::Data->type('xml' => $request), $self->_2fa_header());
    croak "ERRTEXT: " . $som->fault->faultstring if $som->fault;

    my $result = $som->result;
    $self->{result_as_xml} = $result;

    return 1;
}

sub _build_authentication_tag {
    my $self = shift;
    return "<Authentication><Username>$self->{username}</Username><Password>$self->{password}</Password></Authentication>";
}

sub _build_country_code_tag {
    my $self                = shift;
    my $two_digit_country   = $self->{residence};
    my $three_digit_country = uc Locale::Country::country_code2code($two_digit_country, LOCALE_CODE_ALPHA_2, LOCALE_CODE_ALPHA_3);

    croak "Client " . $self->{client_id} . " could not get country from residence [$two_digit_country]" unless $three_digit_country;

    return "<CountryCode>$three_digit_country</CountryCode>";
}

sub _build_person_tag {
    my $self = shift;

    my $dob = $self->{date_of_birth} or croak "No date of birth for " . $self->{client_id};

    if ($dob =~ /^(\d\d\d\d)/) {



( run in 2.143 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )