Dave Cross: Still Munging Data With Perl: Online event - Mar 27 Learn more

use Moose;
use Carp;
use JSON;
use Crypt::JWT qw(encode_jwt);
our $VERSION = '1.000002';
has credentials_path => (
isa => 'Str',
required => 1,
);
has auth_url => (
isa => 'Str',
);
has grant_type => (
isa => 'Str',
default => 'urn:ietf:params:oauth:grant-type:jwt-bearer',
);
has scope => (
isa => 'Str',
);
# so that the token doesn't expire after checking it but before the request
# is processed on the other side.
has token_expiry_shift => (
isa => 'Str',
required => 0,
default => 10,
);
has clock => (
isa => 'CodeRef',
default => sub {sub {time}}
);
has ua_string => (
isa => 'Str',
default => "WWW-Google-Cloud-Auth/$VERSION",
);
has _token => (
is => 'rw',
isa => 'Maybe[Str]',
default => undef,
init_arg => undef,
);
has _token_expiry => (
is => 'rw',
isa => 'Int',
default => 0,
init_arg => undef,
);
has _ua => (
isa => 'LWP::UserAgent',
builder => '_build_ua',
lazy => 1,
init_arg => undef,
);
sub _build_ua {
my $self = shift;
my $ua = LWP::UserAgent->new(
agent => $self->ua_string,
);
return $ua;
}
sub _generate_jwt {
my $self = shift;
open (my $fh, '<', $self->credentials_path) or die("Can't open credentials file: $!");
my $creds_json = do {local $/; <$fh>};
my $creds = JSON::decode_json($creds_json);
my $payload = {
iss => $creds->{client_email},
scope => $self->scope,
exp => $self->clock->() + 600,
iat => $self->clock->(),
};
my $key = $creds->{private_key};
return encode_jwt(alg => 'RS256', payload => $payload, key => \$key);
}
sub get_token {
my $self = shift;
return $self->_token if($self->_token && $self->clock->() < $self->_token_expiry);
my $jwt = $self->_generate_jwt();
my $response = $self->_ua->post(
$self->auth_url,
{
grant_type => $self->grant_type,
assertion => $jwt,
}
);
if ($response->is_success) {
my $r = decode_json($response->decoded_content);
$self->_token($r->{access_token});
$self->_token_expiry($self->clock->() + $r->{expires_in} - $self->token_expiry_shift);
return $self->_token;
} else {
my @err = ($response->code, $response->message, $response->decoded_content);
croak "@err";
}
}
__PACKAGE__->meta->make_immutable;
1;
=pod
=encoding utf8
=head1 NAME
WWW::Google::Cloud::Auth::ServiceAccount - Service account based OAuth authentication for Google Cloud APIs
=head1 SYNOPSIS
my $auth = WWW::Google::Cloud::Auth::ServiceAccount>new(
credentials_path => '/home/myapp/priv/google_some_service.json',
);
my $response = $ua->post(
$some_google_cloud_api_endpoint,
'Content-Type' => 'application/json; charset=utf-8',
'Authorization' => 'Bearer ' . $auth->get_token,
Content => $arguments,
);
=head1 DESCRIPTION
This is a library for service account based OAuth authentication with Google Cloud API endpoints for server to server applications.
=head1 FUNCTIONS
=head2 WWW::Google::Cloud::Auth::ServiceAccount->new(credentials_path => $credentials_path);
Instantiate a new WWW::Google::Cloud::Auth::ServiceAccount object.
Arguments:
=over 4
=item
credentials path [required]
The path to the JSON-encoded credentials file provided by Google.
=item
auth_url [optional]
The URL to get the OAuth token from. Defaults to https://www.googleapis.com/oauth2/v4/token. You probably don't need to change this.
=back
Returns:
=over 4
A new WWW::Google::Cloud::Auth::ServiceAccount instance.
=back
=head2 $auth->get_token()
Get a valid token to use for authorization. If there is a valid cached token return that.
Arguments:
=over 4
None
=back
Returns:
=over 4
The OAuth token
=back
=head1 AUTHOR
This module is written by Larion Garaczi <larion@cpan.org> (2019)
=head1 SOURCE CODE
The source code for this module is hosted on GitHub L<https://github.com/larion/www-google-cloud-auth-serviceaccount>.
Feel free to contribute :)
=head1 LICENSE AND COPYRIGHT
This module is free software and is published under the same
terms as Perl itself.
=cut