Google-Ads-AdWords-Client

 view release on metacpan or  search on metacpan

lib/Google/Ads/Common/OAuth2ServiceAccountsHandler.pm  view on Meta::CPAN

  ATTR(:name<delegated_email_address> :default<>);
my %additional_scopes_of : ATTR(:name<additional_scopes> :default<>);
my %pem_file_of : ATTR(:name<pem_file> :default<>);
my %json_file_of : ATTR(:name<json_file> :default<>);
my %__crypt_module_of : ATTR(:name<__crypt_module> :default<>);

# Constructor
sub START {
  my ($self, $ident) = @_;

  $__crypt_module_of{$ident} ||= "Crypt::OpenSSL::RSA";
}

sub initialize : CUMULATIVE(BASE FIRST) {
  my ($self, $api_client, $properties) = @_;
  my $ident = ident $self;

  $email_address_of{$ident} = $properties->{oAuth2ServiceAccountEmailAddress}
    || $email_address_of{$ident};
  $delegated_email_address_of{$ident} =
       $properties->{oAuth2ServiceAccountDelegateEmailAddress}
    || $delegated_email_address_of{$ident};
  $pem_file_of{$ident} = $properties->{oAuth2ServiceAccountPEMFile}
    || $pem_file_of{$ident};
  $json_file_of{$ident} = $properties->{oAuth2ServiceAccountJSONFile}
    || $json_file_of{$ident};
  $additional_scopes_of{$ident} = $properties->{oAuth2AdditionalScopes}
    || $additional_scopes_of{$ident};
}

sub _refresh_access_token {
  my $self = shift;

  if ($self->get_json_file() and $self->get_pem_file()) {
    warn("Only one of oAuth2ServiceAccountPEMFile or " .
    "oAuth2ServiceAccountJSONFile can be specified in adwords.properties.");
    return 0;
  }

  my $file = $self->__read_certificate_file() || return 0;

  my $iat                     = time;
  my $exp                     = $iat + 3600;
  my $iss                     = $self->get_email_address();
  my $delegated_email_address = $self->get_delegated_email_address();
  my $scope                   = $self->_formatted_scopes();

  my $header = '{"alg":"RS256","typ":"JWT"}';
  my $claims = "{
    \"iss\":\"${iss}\",
    \"sub\":\"${delegated_email_address}\",
    \"scope\":\"${scope}\",
    \"aud\":\"" . OAUTH2_BASE_URL . "/token\",
    \"exp\":${exp},
    \"iat\":${iat}
  }";

  my $encoded_header = __encode_base64_url($header);
  my $encoded_claims = __encode_base64_url($claims);

  my $key = $self->get___crypt_module()->new_private_key($file) || return 0;
  $key->use_pkcs1_padding();
  $key->use_sha256_hash();

  my $signature         = $key->sign("${encoded_header}.${encoded_claims}");
  my $encoded_signature = __encode_base64_url($signature);
  my $assertion = "${encoded_header}.${encoded_claims}.${encoded_signature}";
  my $body =
    "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" .
    "&assertion=" . $assertion;
  push my @headers, "Content-Type" => "application/x-www-form-urlencoded";
  my $request =
    HTTP::Request->new("POST", OAUTH2_BASE_URL . "/token", \@headers, $body);
  my $user_agent = $self->get___user_agent();
  my $res        = $user_agent->request($request);

  if (!$res->is_success()) {
    my $err_msg = $res->decoded_content();
    $self->get_api_client()->get_die_on_faults()
      ? die($err_msg)
      : warn($err_msg);
    return 0;
  }

  my $content_hash = $self->__parse_auth_response($res->decoded_content());

  $self->set_access_token($content_hash->{access_token});
  $self->set_access_token_expires($iat + $content_hash->{expires_in});
}

# Return the private key string from either the PEM file or JSON file specified.
sub __read_certificate_file {
  my $self = shift;
  my $private_key;

  if (!$self->get_pem_file() and !$self->get_json_file()) {
    return 0;
  }

  # JSON File
  if ($self->get_json_file()) {
    my $file_str;
    open(MYFILE, $self->get_json_file()) || return 0;
    while (<MYFILE>) {
      $file_str .= $_;
    }
    my $json_values = parse_json ($file_str);
    $private_key = $json_values->{'private_key'};
    $self->set_email_address($json_values->{'client_email'});
    close(MYFILE);
  }
  # PEM File
  else {
    open(MYFILE, $self->get_pem_file()) || return 0;
    while (<MYFILE>) {
      $private_key .= $_;
    }
    close(MYFILE);
  }

  return $private_key;
}

sub __encode_base64_url($) {
  my ($s) = shift;
  $s = encode_base64($s);
  $s =~ tr{+/}{-_};
  $s =~ s/=*$//;
  $s =~ s/\n//g;
  return $s;
}

sub _scope {
  my $self = shift;
  die "Need to be implemented by subclass";
}

sub _formatted_scopes {
  my $self = shift;
  die "Need to be implemented by subclass";
}

1;

=pod

=head1 NAME

Google::Ads::Common::OAuth2ServiceAccountsHandler

=head1 DESCRIPTION

A generic abstract implementation of L<Google::Ads::Common::OAuth2BaseHandler>
that supports OAuth2 for Service Accounts semantics.

It is meant to be specialized and its L<_scope> and L<_formatted_scopes> methods
be properly implemented.

=head1 ATTRIBUTES

Each of these attributes can be set via
Google::Ads::Common::OAuth2ServiceAccountsHandler->new().

Alternatively, there is a get_ and set_ method associated with each attribute
for retrieving or setting them dynamically.

=head2 api_client

A reference to the API client used to send requests.

=head2 client_id

OAuth2 client id obtained from the Google APIs Console.

=head2 email_address

Service account email address as found in the Google API Console.

=head2 delegated_email_address

Delegated email address of the accounts that has access to the API.



( run in 0.658 second using v1.01-cache-2.11-cpan-13bb782fe5a )