As part of connecting to LinkedIn using LWP::Authen::OAuth2, I have decided to write a sub-class of LWP::Authen::OAuth2::ServiceProvider which is designed to be sub-classed for exactly this kind of application. This way, it will hopefully be useful to other people.
However, I need to override a method which has not been designed to be overridden! The LinkedIn API doesn't comply with the OAuth2 spec. The token_type parameter is mandatory but missing. Therefore, I am trying to inject it into the response from LinkedIn by sub-classing the method, adding the missing parameter and then calling the method in the super class. But I am not managing to get the injection to work.
Here is the sub in LWP::Authen::OAuth2::ServiceProvider that I am overridding:
# Attempts to construct tokens, returns the access_token (which may ha
+ve a
# request token embedded).
sub construct_tokens {
my ($self, $oauth2, $response) = @_;
# The information that I need.
my $content = eval {$response->decoded_content};
if (not defined($content)) {
$content = '';
}
my $data = eval {decode_json($content)};
my $parse_error = $@;
my $token_endpoint = $self->token_endpoint();
# Can this have done wrong? Let me list the ways...
if ($parse_error) {
# "Should not happen", hopefully just network.
# Tell the programmer everything.
my $status = $response->status_line;
return <<"EOT"
Token endpoint gave invalid JSON in response.
Endpoint: $token_endpoint
Status: $status
Parse error: $parse_error
JSON:
$content
EOT
}
elsif ($data->{error}) {
# Assume a valid OAuth 2 error message.
my $message = "OAuth2 error: $data->{error}";
# Do we have a mythical service provider that gives us more?
if ($data->{error_uri}) {
# They seem to have a web page with detail.
$message .= "\n$data->{error_uri} may say more.\n";
}
if ($data->{error_description}) {
# Wow! Thank you!
$message .= "\n\nDescription: $data->{error_description}\n
+";
}
return $message;
}
elsif (not $data->{token_type}) {
# Someone failed to follow the spec...
return <<"EOT";
Token endpoint missing expected token_type in successful response.
Endpoint: $token_endpoint
JSON:
$content
EOT
}
my $type = $self->access_token_class(lc($data->{token_type}));
if ($type !~ /^[\w\:]+\z/) {
# We got an error. :-(
return $type;
}
eval {load($type)};
if ($@) {
# MAKE THIS FATAL. (Clearly Perl code is simply wrong.)
confess("Loading $type for $data->{token_type} gave error: $@"
+);
}
# Try to make an access token.
my $access_token = $type->from_ref($data);
if (not ref($access_token)) {
# This should be an error message of some sort.
return $access_token;
}
else {
# WE SURVIVED! EVERYTHING IS GOOD!
if ($oauth2->access_token) {
$access_token->copy_refresh_from($oauth2->access_token);
}
return $access_token;
}
}
The author has commented the point it fails as
# Someone failed to follow the spec...!
This is my sub that overrides the above...
sub construct_tokens {
my ($self, $oauth2, $response) = @_;
my $content = eval {$response->decoded_content};
eval {decode_json($content)};
$response->push_header( 'token_type', 'Bearer' ) unless $@;
$self->SUPER::construct_tokens($oauth2, $response);
}
I'm trying to set
token_type as
Bearer so that the rest of the sub in the superclass doesn't complain.
Is there a good way to to inject this parameter or am I approaching this in the wrong way?