How do I access the StackExchange API authenticated methods from a perl script? - perl

I'm using WWW::Mechanize. For the methods that do not require authentication, I get those as I would any other url, and then use the perl JSON module to parse out whatever data I want:
my $response = $mech->get('http://api.stackexchange.com/1.1/questions?fromdate=' . $lasthour)
my $q = from_json($response->content())
I've used Mechanize to log into websites in the past, but the Oauth stuff is confusing, and what documentation is provided for using the API suggests that it is intended for web applications (that require registration with StackExchange?).
In particular, I am interested in the notifications method though I would expect the correct code to allow access to any of the auth-required methods.

Have you looked at Net::StackExchange2?
#for methods that require auth.
my $se = Net::StackExchange2->new(
{
site=>"stackoverflow",
access_token => '<THE ACCESS TOKEN>' ,
key => '<YOUR APP KEY>'
}
);
It uses LWP::UserAgent. Even if you don't want to use the Net::StackExchange2 module directly, you have a good chance of finding some good example code to borrow from.

Related

Problem accessing Google API protected with OAuth2 by using LWP::Authen::OAuth2

I am currently coding a service on a perl server, that shall send a request to the Firebase Cloud Messaging API, which will then send push notifications to an app instance.
Since FCM is part of the Google API family, an OAuth2 token is required to access the API. During my research I found this perl solution. Because my service is running on an non-Google server environment, I can't use Google Application Default Credentials, but have to provide them manually, so I downloaded a json containing a private key following this description.
Reading the documentation of LWP::Authen::OAuth2 I got a little confused, where to put which parameter from the json into the $oauth2 object, because often different names are used to reference to the same values, like I suspect.
The json related to my firebase project:
{
"type": "service_account",
"project_id": "my_project_id",
"private_key_id": "some_key_id",
"private_key": "-----BEGIN PRIVATE KEY-----very_long_key-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-o8sf4#<my_project_id>.iam.gserviceaccount.com",
"client_id": "some_client_id",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-o8sf4%40<my_project_id>.iam.gserviceaccount.com"
}
The implementation of the $oauth object looks like this:
my $oauth2 = LWP::Authen::OAuth2->new(
client_id => "Public from service provider",
#probably that will be "some_client_id" from above
client_secret => "s3cr3t fr0m svc prov",
#the "very_long_key"?
service_provider => "Google",
#the "auth_uri"? That's what I would suggest here
#I've read some about the LWP::Authen::OAuth2::ServiceProvider module
#do I have to create an instance of that here?
#if so, which params do I need for that from the json?
redirect_uri => "https://your.url.com/",
#the FCM api I want to call?
# Optional hook, but recommended.
save_tokens => \&save_tokens,
save_tokens_args => [ $dbh ],
# This is for when you have tokens from last time.
token_string => $token_string.
#yes, i copy-pasted that from the docs
);
Now, as a beginner in Perl and a disliker of ambiguous key-values names, i'm a little confused, which value to put where and would be glad if anyone could help me with a guide here, what to put where even if this seems like very rookie question, it is important for me :D. So i'm thankful for every helpful answer!
EDIT
When trying to generate a JSON Web Token manually in my perl service using Crypt::JWT, i came across another trip wire, which made me doubt that the according authentication API from Google "https://www.googleapis.com/auth/firebase.messaging" still accepts Bearer tokens ... I tried the generate my JWT, which seemed to be successful, but the request I sent to the actual FCM API then gave me this:
Request had invalid authentication credentials.
Expected OAuth 2 access token, login cookie
or other valid authentication credential
In the response printed as String I then found this little guy, which confused me a lot:
Client-Warning: Unsupported authentication scheme 'bearer'
Now I'm very unsure, it bearer tokens are still supported for the FCM API, even they are used in an example on the referring docs page. Does anyone have any up-to-date information about that? Thank you very much!
Digging deeper in various documentations and testing some things I realized, that LWP::Authen::OAuth2 is not only a bit of overhead, for creating a very tiny HTTPS request to an OAuth2 protected API, it is currently not possible either.
The caveat is hidden in the service_provider, who hosts the authentication API, that I have to call to authenticate and authorize my service for accessing the actual Firecase Cloud Messaging API. In my case, this is Google, more precisely https://www.googleapis.com/auth/firebase.messaging, also referred to as scope in code.
Now one service provider can provide different authentication APIs for different clients. That's why some service providers require an additional parameter - the client_type or type respectively (for unambiguity I will use client_type) - that also affects the authentication and authorization process via OAuth2.
When creating a new $oauth2-object, and assigning a value to the field service_provider an object of module LWP::Authen::OAuth2::ServiceProvider gets created, that may need also its parameters, like the scope, to define, for which API family you want to be authorized and depending on that a client_type.
Now Google is not a no-name Service Provider, so there already is a prebuild ServiceProvider module, explicitily for Google APIs : LWP::Authen::OAuth2::ServiceProvider::Google. This automatically fills in some parameters of a ServiceProvider object, and holds a hash of available client_types to make sure you use one of them, because depending on the client_type a specific sub-module of ServiceProvider::Google gets created internally.
So I tried to test that like so:
my $oauth2 = LWP::Authen::OAuth2->new(
service_provider => "Google",
scope => "https://www.googleapis.com/auth/firebase.messaging",
client_type => "service_account", #referring to 'type' in the json
client_id => "Public from service provider",
client_secret => "s3cr3t fr0m svc prov",
redirect_uri => "this-server.mydomain.com"
);
Follwing the further description here and sending a request with the built-in LWP::UserAgent object I still got an 401 error UNAUTHENTICATED which confused me a lot. So when I read the documentation the I-don't-know-what time I stepped across this tiny line in the client_types chapter of ServiceProvider::Google:
Service Account
This client_type is for applications that login to the developer's account using the developer's credentials. See https://developers.google.com/accounts/docs/OAuth2ServiceAccount for Google's documentation.
This is not yet supported, and would require the use of JSON Web Tokens to support.
Yep, that kinda knocks out the usage of the whole LWP:Authen::OAuth family to access Firebase APIs, as almost all of them have the client_type => "service_account". Now I have a deadline for my project and can't wait for the module to get extended or extending it myself would exceed my perl skills.
But between tears of desperation there is always a glimpse of hope, as I found this Google docs page with a live-saving addendum, that it's possible to use a JSON Web Token as a Bearer token. Researching for that I found more than one Perl solution to generate JWT from JSONs like a service account, and also this very helpful Stackoverflow answer that showed me the way out of the choke point.

How to get argument when POSTed to URL using Perl (Mojolicious)

I am setting up duo two-factor authentication in a perl web application (using mojolicious). I am new to perl so this may be a simple answer.
I have everything set up except I need to verify the response by doing the following:
"After the user authenticates (e.g. via phone call, SMS passcode, etc.) the IFRAME will generate a signed response called sig_response and POST it back to the post_action URL. Your server-side code should then call verify_response() to verify that the signed response is legitimate."
In perl, how can you call for sig_response, is there a module? Below is in example using python:
sig_response = self.get_argument("sig_response") # for example (if using Tornado: http://www.tornadoweb.org/en/stable/documentation.html)
Duo Web: https://duo.com/docs/duoweb
It looks like this sig_response is just a value that's POSTed to your response handler. When you created the URL to show in the iframe, it had a post_action parameter. That's the endpoint in your application that handles the user coming back from the iframe.
You need to build a route in Mojo for that. There, you need to look at the parameters you are receiving in the POST body. I don't know if it's form-data or something else, like JSON. It doesn't really say in the documentation. I suggest you dump the parameters, and if that doesn't show it, dump the whole request body.
Once you have that sig_response parameter, you need to call the verify_response function that duo's library provides and look at the return value.
If you have not done it yet, get the SDK at https://github.com/duosecurity/duo_perl. It's not a full distribution. You can either clone the whole thing or just download the pm file from their github and put it in the lib directory of your application. Then use it like you would any other module. Since it doesn't export anything, you need to use the fully qualified name to call the verify_response function.
The whole thing might look something like this untested code:
post '/duo_handler' => sub {
my $c = shift;
my $sig_request = $c->param('sig_response');
my $user = DuoWeb::verify_response($ikey, $skey, $akey, $sig_request);
if ($user) {
# logged in
} else {
# not logged in
}
};
Disclaimer: I don't know this service. I have only quickly read the documentation you linked, and taken a look at their Perl SDK, which they should really put on CPAN.

Replace authenticated user agent login/ page scrape using Perl and Mojolicious

I am trying to port some old web scraping scripts written using older Perl modules to work using only Mojolicious.
Have written a few basic scripts with Mojo but am puzzled on an authenticated login which uses a secure login site and how this should be handled with a Mojo::UserAgent script. Unfortunately the only example I can see in the documentation is for basic authentication without forms.
The Perl script I am trying to convert to work with Mojo:UserAgent is as follows:
#!/usr/bin/perl
use LWP;
use LWP::Simple;
use LWP::Debug qw(+);
use LWP::Protocol::https;
use WWW::Mechanize;
use HTTP::Cookies;
# login first before navigating to pages
# Create our automated browser and set up to handle cookies
my $agent = WWW::Mechanize->new();
$agent->cookie_jar(HTTP::Cookies->new());
$agent->agent_alias( 'Windows IE 6' ); #tell the website who we are (old!)
# get login page
$agent->get("https://reg.mysite.com")
$agent->success or die $agent->response->status_line;
# complete the user name and password form
$agent->form_number (1);
$agent->field (username => "user1");
$agent->field (password => "pass1");
$agent->click();
#try to get member's only content page from main site on basis we are now "logged in"
$agent->get("http://www.mysite.com/memberpagesonly1");
$agent->success or die $agent->response->status_line;
$member_page = $agent->content();
print "$member_page\n";
So the above works fine. How to convert to do the same job in Mojolicious?
Mojolicious is a web application framework. While Mojo::UserAgent works well as a low-level HTTP user agent, and provides facilities that are unavailble from LWP (in particular native support for asynchronous requests and IPV6) neither are as convenient to use as as WWW::Mechanize for web scraping.
WWW::Mechanize subclasses LWP::UserAgent to interface with the internet, and uses HTML::Form to process the forms it finds. Mojo::UserAgent has no facility for processing HTML forms, and so building the corresponding HTTP requests is not at all straighforward. Information such as the HTTP method used (GET or POST) the names of the form fields, and the insertion of default values for hidden fields are all done automatically by HTML::Form and are left to the programmer if you restrict yourself to Mojo::UserAgent.
It seems to me that even trying to use Mojo::UserAgent in combination with HTML::Form is poblematic, as the former requires a Mojo::Transaction::HTTP object to represent the submission of a filled-in form, whereas the latter generates HTTP::Request objects for use with LWP.
In short, unless you are willing to largely rewrite WWW::Mechanize, I think there is no way to reimplement your software using Mojolicious modules.
You can use WWW::Mechanize to talk to web servers, and you can use Mojo::DOM to benefit from Mojolicious' as a parser. The best of two worlds... :)

How to Use apiHttpClient in Google PHP API Client

I have been beating my head around this one; the examples provided in the PHP client library under "apps" shows how to use the apiHttpRequest class to make HTTP request to the Provisioning API. The example shows how to list and delete a user in a domain (that is apps enabled) but I am having a had time understanding how to POST to the provision API (feeds) because I will like to programatically create groups using the apiHttpRequest. Any help on this will be highly appreciated!
I have include a code sample for deleting a user
// Deleting a User Alias from a Domain (Experimental)
$domain = "example.com";
$user = rawurlencode("user#domain.com");
$req = new apiHttpRequest("https://apps-apis.google.com/a/feeds/alias/2.0/$domain/$user", 'DELETE');
$resp = $client::getIo()->authenticatedRequest($req);
print "<h1>Deleting a User Alias from a Domain</h1>: <pre>" . $resp->getResponseBody() . "</pre>";
Oh! I see, one has to create an Atom XML mockup and pass it as a string. Is there a more elegant way of handling this? We are not using Zend and so we have to create this ourselves.

Zend Framework Twitter Service Requires Authentication for UserTimeline?

I am using the Zend_Service_Twitter class to retrieve my userTimeline, however since August, I am no longer able to use Basic Authentication. This is perfectly fine, since userTimeline does not require authentication at all.
Strange enough, Zend_Service_Twitter thinks userTimeline requires full authentication (using oAuth tokens) to retrieve an unprotected twitter userTimeline. Is there a way around this that allows me to use all the normal userTimeline twitter api variables.
Thanks
I recently ran into this same issue - however, instead of trying to fix the broken Zend class, I just used the Search API.
$searchString = 'from:leeked';
$twitterSearch = new Zend_Service_Twitter_Search('json');
$this->twitter = $twitterSearch->search($searchString, array('rpp' => 15));