Subject: | Bug in LWP::Authen::OAuth2::AccessToken refresh code |
Date: | Wed, 20 May 2020 00:49:02 -0400 |
To: | "bug-LWP-Authen-OAuth2" <bug-LWP-Authen-OAuth2 [...] rt.cpan.org> |
From: | "Ruud A Haring" <ruud [...] us.ibm.com> |
Hi,
Have been using a Perl script based on LWP::Authen::OAuth2 for a long
time to run nightly maintenance on data we have at an external service
provider (Box.com).
Works great -- thanks!
Once initial authentication has been set up via web browser (i.e.
manually), I use save_tokens to save off the $token_string to a file (
oauth2_tokens ).
From that point onwards I have been running jobs automatically (cron
jobs): each new job picks up the previous job's $token_string from the
file, refreshes the tokens and saves the new $token_string off again.
With the refresh_token being valid for up to 60 days, this scheme enables
automated nightly runs -- but multiple jobs per night need to run
sequentially: 1 job at a time.
With workload increasing, I was intrigued to read in
https://metacpan.org/pod/LWP::Authen::OAuth2 about the prerefresh
handler:
"A handler to be called before attempting to refresh tokens. It is passed
the $oauth2 object. If it returns a token string, that will be used to
generate tokens instead of going to the service provider.
The purpose of this hook is so that, even if you have multiple processes
accessing an API simultaneously, only one of them will try to refresh
tokens with the service provider.
(Service providers may dislike having multiple refresh requests arrive at
once from the same consumer for the same user.)"
This appears to enable token management for parallel operations of
multiple jobs, sharing tokens as long as they are valid, and refreshing
tokens only once for all parallel jobs, when the tokens are about to
expire.
My scenario:
some long-running process A is starting with a fresh set of
tokensA;
some process B can run in parallel to A, re-using tokensA (if
enough time before expiration);
some process C starts later with not enough time before tokensA
is about to expire. So it refreshes the tokens to tokensC; which
immediately invalidates tokensA;
process A (if still running) now stalls and needs to pick up the
newest token set (tokensC).
Note 1: The documentation does not describe that method
$oauth2->refresh_access_token() is required to cause a call to
prerefresh() -- if I need that in the program's initial refresh_tokens
operation.
My original call: $oauth2->request_tokens( grant_type =>
'refresh_token', 'refresh_token' => $refresh_token ) bypassed
prerefresh().
Note 2: The documentation does not describe how (with what parameters) the
prerefresh() callback function is called
I found that the following works:
sub prerefresh {
my ( $oauth2, $token_type, $current_refresh_token ) = @_; # $oauth2
object; $token_type='refresh_token'
...
Note 3: If prerefresh() returns nothing, LWP::Authen::OAuth2 executes
the token refresh with the service provider, and everything works fine.
However, if prerefresh() (invoked by
$oauth2->refresh_access_token() ) returns the JSON $token_string
(straight from the save_tokens file)
then OAuth2 crashes with:
Can't call method "request" on unblessed reference at
/usr/local/share/perl5/site_perl/5.26/LWP/Authen/OAuth2.pm line 104.
Under another scenario ($oauth2->get() on process A running into
the above token-invalidation by another process ) I got:
Can't call method "expires_in" on unblessed reference at
/usr/local/share/perl5/site_perl/5.26/LWP/Authen/OAuth2/AccessToken.pm
line 166.
I'm afraid that I can't lay my finger on what causes this unblessed
reference to pop up, when prerefresh returns a token string.
Any suggestions appreciated.
Environment:
Windows 10/Cygwin -- my debug env't (production is on AIX)
perl 5, version 26, subversion 3 (v5.26.3) built for
x86_64-cygwin-threads-multi
LWP::Authen::OAuth2 : our $VERSION = '0.16';
LWP::Authen::OAuth2::AccessToken: our $VERSION = '0.02';
Thanks so much,
Ruud.
---------------------------
Ruud A. Haring,
Principal Research Staff Member,
IBM Thomas J. Watson Research Center,
P.O. Box 218, Yorktown Heights, NY 10598, USA.
phone: +1-914-945-2856
e-mail: ruud@us.ibm.com