Is there any method for getting expiry date of ssl certificate in Perl? - perl

I use LWP::UserAgent, Date::Parse,Net::SSLeay qw(get_https3) for expiry date of ssl certificate it gives me 3 dates in which 1 is correct
I tried this method for fetching dates but it gives me 3 dates for this where one is correct and for other dates it gives wrong dates total I have to get dates of 20 urls it gives me 3 correct date and other are wrong and also it gives me 2-3 dates for per url
my $ua = LWP::UserAgent->new(
ssl_opts => {
SSL_verify_callback => sub {
my ($ok, $ctx_store) = #_;
my $cert = Net::SSLeay::X509_STORE_CTX_get_current_cert($ctx_store);
print Net::SSLeay::P_ASN1_TIME_get_isotime(
Net::SSLeay::X509_get_notAfter($cert) ), "\n";
return $ok;
},
},
);
my $url = 'https://pjf.nif.org/m/login';
I want a perfect date as shown in certification option on any url and

For the URL that you show in your example, there are three certificates: the root certificate from USERTrust, the intermediate certificate from Sectigo, and the website certificate. The dates correspond to the expiration of each of those. The Difference Between Root Certificates and Intermediate Certificates is a good read.
You likely want the closest date, which should be the expiry of the user certificate.

Related

Google currency converter no longer working

It appears that Google Finance Currency Converter has stopped working altogether. A week ago I started getting these email notifications from my Magento 1.9.2 store:
Currency update warnings:
WARNING: Cannot retrieve rate from https://finance.google.com/finance/converter?a=1&from=GBP&to=EUR.
WARNING: Cannot retrieve rate from https://finance.google.com/finance/converter?a=1&from=GBP&to=USD.
Those URLs are indeed no longer valid. Does anyone know if there are new URLs we can use, or do we need to configure a different service?
This link is not working anymore.
protected $_url = 'https://finance.google.com/finance/converter?a=1&from={{CURRENCY_FROM}}&to={{CURRENCY_TO}}';
I researched and found this codes.
Find this file:
app/code/local/Payserv/GoogleFinance/Model/Google.php
Replace the codes with this:
class Payserv_GoogleFinance_Model_Google extends Mage_Directory_Model_Currency_Import_Abstract {
protected $_url = 'http://free.currencyconverterapi.com/api/v3/convert?q={{CURRENCY_FROM}}_{{CURRENCY_TO}}';
protected $_messages = array();
protected function _convert($currencyFrom, $currencyTo, $retry=0) {
$url = str_replace('{{CURRENCY_FROM}}', $currencyFrom, $this->_url);
$url = str_replace('{{CURRENCY_TO}}', $currencyTo, $url);
try {
$resultKey = $currencyFrom.'_'.$currencyTo;
$response = file_get_contents($url);
$data = Mage::helper('core')->jsonDecode($response);
$results = $data['results'][$resultKey];
$queryCount = $data['query']['count'];
if( !$queryCount && !isset($results)) {
$this->_messages[] = Mage::helper('directory')->__('Cannot retrieve rate from %s.', $url);
return null;
}
return (float)$results['val'];
} catch (Exception $e) {
if ($retry == 0) {
$this->_convert($currencyFrom, $currencyTo, 1);
} else {
$this->_messages[] = Mage::helper('directory')->__('Cannot retrieve rate from %s', $url);
}
}
}
}
It seems to be intermittent (it shows if I load a page 10 times or so, but only once every 10 clicks). But I've personally started configuring other services. I am using bank API's (currently a Swedish one so it might not help you). But check with your bank, they usually have APIs.
Good luck!
Apparently Google doesn't offer this service anymore.
The main alternative looks to be:
Fixer.io API
Currencylayer API
Both offers 1000 request for free a month ( you need to create an account on their homepage )
Source: https://stackoverflow.com/a/8391430/716435
Google doesn't provide the currency converter API anymore. There are several alternative APIs offering currency conversion data. Some have been mentioned in posts already (Fixer, Currencylayer...)
Another option is SWOP currency exchange rate API, a fast, easy to use, reliable and transparent foreign exchange rate API made from developers for developers. Full disclaimer: I'm one of the developers of SWOP :)
The SWOP API offers current and historical rates for 180+ currencies. They are gathered directly from trusted sources (various Central Banks and other important banks).
The SWOP API has two endpoints, GraphQL and REST/JSON, for developer convenience.
There's a free plan allowing 1,000 requests per month.
The problem is with the link, google updated the api link recently, and I found success once on checking 10 times to the existing link. Try changing to this link https://www.google.com/finance/converter
see this https://www.techbuy.in/google-finance-api-currency-converter-not-working-updated-link-check-currency-converter/
I was facing same problem from last week. But new url solved my problem and now currency conversion working fine.
try this:
https://finance.google.com/bctzjpnsun/converter
Google's finance URL doesn't seem to work for now, I have prepared a workaround to use MSN Money (Microsoft's) API. It returns JSON so you can consume it using any programing language, I have put sample using PHP:
function msn($from, $to, $amount) {
$url = 'https://finance.services.appex.bing.com/Market.svc/ChartDataV5?symbols=245.20.'.strtoupper($from).strtoupper($to).'LITE&chartType=1y';
$request = curl_init();
$timeOut = 0;
curl_setopt($request, CURLOPT_URL, $url);
curl_setopt($request, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($request, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1)');
curl_setopt($request, CURLOPT_CONNECTTIMEOUT, $timeOut);
$response = json_decode(curl_exec($request));
curl_close($request);
$rate = array_last($response[0]->Series)->P;
return $rate * $amount;
}
The above function accepts the currency that you currently have, the target currency and amount. Send's a GET request to MSN URL and parses the JSON to get today's exchange rate. Finally, it multiplies the rate with your amount to convert it to the target currency.
I hope this solves your need, the code has a lot of rooms for optimization I just gave you a simple implementation
For example, you can save the exchange rate in your database and use that rate for one day this way you will only call the API once a day.

How to delete a session in cgi-perl?

I have a perl-cgi script through which I am trying to log in.
When the UserName and password are valid, I create a session and redirect a cookie to another page.
However, after the session expires(I have set the expiration time), I do not see it get deleted from the /tmp/sessions folder in this case. I have used the command to delete the session as well.
Can someone help me to delete the session once it expires? Also, does the cookie expire once the session is deleted?
use CGI::Session;
use CGI::Session::Tutorial;
use CGI::Session::Driver::file;
use CGI::Cookie;
my $session = new CGI::Session("driver:File", undef, {Directory=>"/tmp/sessions"});
my $sid = $session->id();
#my $cookie = $query->cookie(CGISESSID => $session->id);
my $cookie = $query->cookie(-name=>"CGISESSID",
-value=>$session->id,
-domain=>'abc.com',
-expires=>"+5m",
-path=>"/");
print $query->redirect(-uri => 'http://abc.cgi', -cookie => $cookie);
$session->param("UserName", $loginUserName);
$query->hidden( 'UserName', $loginUserName );
$session->expire("UserName",'1m');
$session->expire('+5m');
$session->delete();
To avoid confusion with ->delete, I'm going to use the word "remove" instead of "delete" to refer to the removal of the session from storage.
Can someone help me to delete the session once it expires?
The removal doesn't happen when the session expires. That would require having a continually running process. Furthermore, at no point does CGI::Session scan storage for expired sessions; that would take too long since it would require loading each and every session. Instead, CGI::Session only removes expired sessions when you try to load them.
#!/usr/bin/perl
use strict;
use warnings qw( all );
use feature qw( say );
use CGI::Session qw( );
use CGI::Session::Driver::file qw( );
my $session_id; # This represents the browser's cookie.
# These represent requests made the by the browser.
for my $request_num (1..3) {
my $session = CGI::Session->new("driver:file", $session_id, { Directory => "/tmp/sessions" });
$session->expire("1s");
$session_id = $session->id; # This represents setting the browser's cookie.
say "$request_num: ", $session->id;
say "$request_num: ", $session->param("foo") // "[undef]";
$session->param("foo" => "bar");
# This represents time passing by before the third request.
if ($request_num == 2) {
say "Letting session expire...";
sleep(2);
}
}
Output:
$ ./a
1: c57ab28952c6ed422c15f1a223f4b45d
1: [undef]
2: c57ab28952c6ed422c15f1a223f4b45d
2: bar
Letting session expire...
3: df8ba3b66f23a9a2a652520fa6b4c30b
3: [undef]
$ ls -1 /tmp/sessions
cgisess_df8ba3b66f23a9a2a652520fa6b4c30b
If you want to prevent files from accumulating on your drive, create a cron job that deletes old files.
find /tmp/sessions -mindepth 1 -maxdepth 1 -mtime 7 -delete
Also, does the cookie expire once the session is deleted?
No, the cookie expires when you tell it to expire. The thing is, it doesn't matter if the browser's cookie expires or not. For the second argument of new, there's no difference between passing undef, passing the id of a deleted session and passing the id of an expired session; you'll get a new session in all three cases. If anything, it's actually better if it doesn't expire as soon as the session expires because this allows the session to be removed (as demonstrated above).
How to delete a session in cgi-perl?
$session->delete is indeed the way to go, but the actual removal only happens when you would save (flush) the session.
$session->delete();
$session->flush(); # Or let `$session` get destroyed.
As the documentation notes
delete()
Sets the objects status to be "deleted". Subsequent read/write requests on the same object will fail. To physically delete it from the data store you need to call flush(). CGI::Session attempts to do this automatically when the object is being destroyed (usually as the script exits), but see "A Warning about Auto-flushing". (emphases mine)
You go on to ask:
Also, does the cookie expire once the session is deleted?
Of course not. You already sent a cookie to the user's browser with an expiration time of five minutes in the future. The cookie will expire then.
If, in the mean time, you have forced the expiration of the session on the server, the user's browser will still send the previously received cookie. Your application will just not find a session corresponding to the session identifier stored in the cookie.
You really need to understand the HTTP request/response cycle before taking one more step.
Per the CGI::Session documentation, deleteing a session "Sets the objects status to be "deleted". Subsequent read/write requests on the same object will fail. To physically delete it from the data store you need to call flush()." (emphasis mine)
Also, per the CGI::Session::Tutorial, "Expiring a session is the same as deleting it via delete(), but deletion takes place automatically." It is not necessary (or useful) to delete a session after it has expired.

Mojolicious session expires versus default_expiration

I have a program where I want the session data to expire at an absolute Epoch time after a request. I don't want the expiration to update for every request.
default_expiration works but not expires.
This does not work:
post '/access' => sub {
my $self = shift;
my $user = $self->param('username');
if ($self->authenticate($user, $self->param('password'))) {
### Set this otherwise timeout refreshes for every request...
$self->session(expires => time + 120);
(...)
}
};
Mojolicious cookie set to 1 hour (3600s) the default...
$self->sessions->default_expiration(120) works but reset for every browser request.
I am using the Mojolicious::Plugin::Authentication plugin.
You can use the expiration key to set the same value as the default_expiration does. Before you set the value though, be sure to check to see if the key exists already and don't overwrite it if it does.

How can I read multiple cookies with Perl's CGI.pm?

I am using CGI.pm to write out cookies. Now during the course of the user using my site, other cookies are added to the "test.com" cookie set, (as shown in the broswer history)
But now I want to log the user out, and "clean" the PC. Since I don't know what scripts the user has used, I can't foresee what cookies would be on the PC.
In short, it there a way to read all the cookies for "test.com" back into a script so I can then print them out again with a 1s duration, (effectively 'deleting' them) ** I know you can read the cookie back in with $xyz=cookie('$name') ... but how can I create the array holding the $name variable so I can loop through it? The script will also run on "test.com", so the cross site policy is not an issue
+++++
brian d foy added a partial answer below. So this how I envisage the code might be strung together.
use CGI::Cookie;
%cookies = CGI::Cookie->fetch;
for (keys %cookies) {
$del_cookie.="cookie(-NAME=>'$cookies[$_]',-PATH=>'/',-EXPIRES=>'+1s');";
}
print header(-cookie=>[$del_cookie]);
I wondered how the script would recognise the domain. Appears the script is intelligent enough to only load the cookies for the domain for which the script is being executed on. (Now I've just got to find out why Firefox doesn't delete expired cookies!! Just found some listed that expired 29th - 31st Jan within my test domain, and at first wondered why they didn't appear in my cookie list!)
If you are trying to do this from your CGI script, you'll only have access to the cookies for that domain. You can get that list and reset them by giving them a time in the past.
It sounds like you aren't asking a cookie question at all. You're asking how to make an array. The CGI::Cookies (which comes with CGI.pm) has an example to deal with all the cookies you have access to under that domain:
%cookies = CGI::Cookie->fetch;
for (keys %cookies) {
do_something($cookies{$_});
}
This is what I ended up with:
use CGI::Cookies;
%cookies = CGI::Cookie->fetch;
#cookie = keys %cookies;
for($x=0; $x<#cookie; $x++){
my $c = CGI::Cookie->new(-name => $cookie[$x],-value => '-',-expires => '+1s');
print "Set-Cookie: $c\n";
}
print "content-type: text/html\n\n";
Firefox still leaves the cookies intact, (apparently that's a "design issue" and not a bug!!) but they are reset to a void value, and set to expire / become redundant in 1 second. Plus, quite why the "print" statement being sent before the "content-type" header doesn't cause a server error I don't know. OK, so purists will probably find a simpler system, and use "foreach" rather than for/next loop ... but I understand how the latter works!

Not receiving Google OAuth refresh token

I want to get the access token from Google. The Google API says that to get the access token, send the code and other parameters to token generating page, and the response will be a JSON Object like :
{
"access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74"
}
However, I'm not receiving the refresh token. The response in my case is:
{
"access_token" : "ya29.sddsdsdsdsds_h9v_nF0IR7XcwDK8XFB2EbvtxmgvB-4oZ8oU",
"token_type" : "Bearer",
"expires_in" : 3600
}
The refresh_token is only provided on the first authorization from the user. Subsequent authorizations, such as the kind you make while testing an OAuth2 integration, will not return the refresh_token again. :)
Go to the page showing Apps with access to your account:
https://myaccount.google.com/u/0/permissions.
Under the Third-party apps menu, choose your app.
Click Remove access and then click Ok to confirm
The next OAuth2 request you make will return a refresh_token (providing that it also includes the 'access_type=offline' query parameter.
Alternatively, you can add the query parameters prompt=consent&access_type=offline to the OAuth redirect (see Google's OAuth 2.0 for Web Server Applications page).
This will prompt the user to authorize the application again and will always return a refresh_token.
In order to get the refresh token you have to add both approval_prompt=force and access_type="offline"
If you are using the java client provided by Google it will look like this:
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, getClientSecrets(), scopes)
.build();
AuthorizationCodeRequestUrl authorizationUrl =
flow.newAuthorizationUrl().setRedirectUri(callBackUrl)
.setApprovalPrompt("force")
.setAccessType("offline");
I'd like to add a bit more info on this subject for those frustrated souls who encounter this issue. The key to getting a refresh token for an offline app is to make sure you are presenting the consent screen. The refresh_token is only returned immediately after a user grants authorization by clicking "Allow".
The issue came up for me (and I suspect many others) after I'd been doing some testing in a development environment and therefore already authorized my application on a given account. I then moved to production and attempted to authenticate again using an account which was already authorized. In this case, the consent screen will not come up again and the api will not return a new refresh token. To make this work, you must force the consent screen to appear again by either:
prompt=consent
or
approval_prompt=force
Either one will work but you should not use both. As of 2021, I'd recommend using prompt=consent since it replaces the older parameter approval_prompt and in some api versions, the latter was actually broken (https://github.com/googleapis/oauth2client/issues/453). Also, prompt is a space delimited list so you can set it as prompt=select_account%20consent if you want both.
Of course you also need:
access_type=offline
Additional reading:
Docs: https://developers.google.com/identity/protocols/oauth2/web-server#request-parameter-prompt
Docs: https://developers.google.com/identity/protocols/oauth2/openid-connect#re-consent
Discussion about this issue: https://github.com/googleapis/google-api-python-client/issues/213
I searched a long night and this is doing the trick:
Modified user-example.php from admin-sdk
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
$authUrl = $client->createAuthUrl();
echo "<a class='login' href='" . $authUrl . "'>Connect Me!</a>";
then you get the code at the redirect url
and the authenticating with the code and getting the refresh token
$client()->authenticate($_GET['code']);
echo $client()->getRefreshToken();
You should store it now ;)
When your accesskey times out just do
$client->refreshToken($theRefreshTokenYouHadStored);
This has caused me some confusion so I thought I'd share what I've come to learn the hard way:
When you request access using the access_type=offline and approval_prompt=force parameters you should receive both an access token and a refresh token. The access token expires soon after you receive it and you will need to refresh it.
You correctly made the request to get a new access token and received the response that has your new access token. I was also confused by the fact that I didn't get a new refresh token. However, this is how it is meant to be since you can use the same refresh token over and over again.
I think some of the other answers assume that you wanted to get yourself a new refresh token for some reason and sugggested that you re-authorize the user but in actual fact, you don't need to since the refresh token you have will work until revoked by the user.
Rich Sutton's answer finally worked for me, after I realized that adding access_type=offline is done on the front end client's request for an authorization code, not the back end request that exchanges that code for an access_token. I've added a comment to his answer and this link at Google for more info about refreshing tokens.
P.S. If you are using Satellizer, here is how to add that option to the $authProvider.google in AngularJS.
In order to get the refresh_token you need to include access_type=offline in the OAuth request URL. When a user authenticates for the first time you will get back a non-nil refresh_token as well as an access_token that expires.
If you have a situation where a user might re-authenticate an account you already have an authentication token for (like #SsjCosty mentions above), you need to get back information from Google on which account the token is for. To do that, add profile to your scopes. Using the OAuth2 Ruby gem, your final request might look something like this:
client = OAuth2::Client.new(
ENV["GOOGLE_CLIENT_ID"],
ENV["GOOGLE_CLIENT_SECRET"],
authorize_url: "https://accounts.google.com/o/oauth2/auth",
token_url: "https://accounts.google.com/o/oauth2/token"
)
# Configure authorization url
client.authorize_url(
scope: "https://www.googleapis.com/auth/analytics.readonly profile",
redirect_uri: callback_url,
access_type: "offline",
prompt: "select_account"
)
Note the scope has two space-delimited entries, one for read-only access to Google Analytics, and the other is just profile, which is an OpenID Connect standard.
This will result in Google providing an additional attribute called id_token in the get_token response. To get information out of the id_token, check out this page in the Google docs. There are a handful of Google-provided libraries that will validate and “decode” this for you (I used the Ruby google-id-token gem). Once you get it parsed, the sub parameter is effectively the unique Google account ID.
Worth noting, if you change the scope, you'll get back a refresh token again for users that have already authenticated with the original scope. This is useful if, say, you have a bunch of users already and don't want to make them all un-auth the app in Google.
Oh, and one final note: you don't need prompt=select_account, but it's useful if you have a situation where your users might want to authenticate with more than one Google account (i.e., you're not using this for sign-in / authentication).
1. How to get 'refresh_token' ?
Solution: access_type='offline' option should be used when generating authURL.
source : Using OAuth 2.0 for Web Server Applications
2. But even with 'access_type=offline', I am not getting the 'refresh_token' ?
Solution: Please note that you will get it only on the first request, so if you are storing it somewhere and there is a provision to overwrite this in your code when getting new access_token after previous expires, then make sure not to overwrite this value.
From Google Auth Doc : (this value = access_type)
This value instructs the Google authorization server to return a
refresh token and an access token the first time that your application
exchanges an authorization code for tokens.
If you need 'refresh_token' again, then you need to remove access for your app as by following the steps written in Rich Sutton's answer.
I'm using nodejs client for access to private data
The solution was add the promp property with value consent to the settings object in oAuth2Client.generateAuthUrl function.
Here is my code:
const getNewToken = (oAuth2Client, callback) => {
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
prompt: 'consent',
scope: SCOPES,
})
console.log('Authorize this app by visiting this url:', authUrl)
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
})
rl.question('Enter the code from that page here: ', (code) => {
rl.close()
oAuth2Client.getToken(code, (err, token) => {
if (err) return console.error('Error while trying to retrieve access token', err)
oAuth2Client.setCredentials(token)
// Store the token to disk for later program executions
fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
if (err) return console.error(err)
console.log('Token stored to', TOKEN_PATH)
})
callback(oAuth2Client)
})
})
}
You can use the online parameters extractor to get the code for generate your token:
Online parameters extractor
Here is the complete code from google official docs:
https://developers.google.com/sheets/api/quickstart/nodejs
I hope the information is useful
Setting this will cause the refresh token to be sent every time:
$client->setApprovalPrompt('force');
an example is given below (php):
$client = new Google_Client();
$client->setClientId($client_id);
$client->setClientSecret($client_secret);
$client->setRedirectUri($redirect_uri);
$client->addScope("email");
$client->addScope("profile");
$client->setAccessType('offline');
$client->setApprovalPrompt('force');
For me I was trying out CalendarSampleServlet provided by Google. After 1 hour the access_key times out and there is a redirect to a 401 page. I tried all the above options but they didn't work. Finally upon checking the source code for 'AbstractAuthorizationCodeServlet', I could see that redirection would be disabled if credentials are present, but ideally it should have checked for refresh token!=null. I added below code to CalendarSampleServlet and it worked after that. Great relief after so many hours of frustration . Thank God.
if (credential.getRefreshToken() == null) {
AuthorizationCodeRequestUrl authorizationUrl = authFlow.newAuthorizationUrl();
authorizationUrl.setRedirectUri(getRedirectUri(req));
onAuthorization(req, resp, authorizationUrl);
credential = null;
}
Using offline access and prompt:consent worked well to me:
auth2 = gapi.auth2.init({
client_id: '{cliend_id}'
});
auth2.grantOfflineAccess({prompt:'consent'}).then(signInCallback);
In order to get new refresh_token each time on authentication the type of OAuth 2.0 credentials created in the dashboard should be "Other". Also as mentioned above the access_type='offline' option should be used when generating the authURL.
When using credentials with type "Web application" no combination of prompt/approval_prompt variables will work - you will still get the refresh_token only on the first request.
To get a refresh token using postman, here is an example of the configurations
Expected Response
now google had refused those parameters in my request (access_type, prompt)... :( and there is no "Revoke Access" button at all. I'm frustrating because of getting back my refresh_token lol
UPDATE:
I found the answer in here :D you can get back the refresh token by a request
https://developers.google.com/identity/protocols/OAuth2WebServer
curl -H "Content-type:application/x-www-form-urlencoded" \
https://accounts.google.com/o/oauth2/revoke?token={token}
The token can be an access token or a refresh token. If the token is an access token and it has a corresponding refresh token, the refresh token will also be revoked.
If the revocation is successfully processed, then the status code of the response is 200. For error conditions, a status code 400 is returned along with an error code.
#!/usr/bin/env perl
use strict;
use warnings;
use 5.010_000;
use utf8;
binmode STDOUT, ":encoding(utf8)";
use Text::CSV_XS;
use FindBin;
use lib $FindBin::Bin . '/../lib';
use Net::Google::Spreadsheets::V4;
use Net::Google::DataAPI::Auth::OAuth2;
use lib 'lib';
use Term::Prompt;
use Net::Google::DataAPI::Auth::OAuth2;
use Net::Google::Spreadsheets;
use Data::Printer ;
my $oauth2 = Net::Google::DataAPI::Auth::OAuth2->new(
client_id => $ENV{CLIENT_ID},
client_secret => $ENV{CLIENT_SECRET},
scope => ['https://www.googleapis.com/auth/spreadsheets'],
);
my $url = $oauth2->authorize_url();
# system("open '$url'");
print "go to the following url with your browser \n" ;
print "$url\n" ;
my $code = prompt('x', 'paste code: ', '', '');
my $objToken = $oauth2->get_access_token($code);
my $refresh_token = $objToken->refresh_token() ;
print "my refresh token is : \n" ;
# debug p($refresh_token ) ;
p ( $objToken ) ;
my $gs = Net::Google::Spreadsheets::V4->new(
client_id => $ENV{CLIENT_ID}
, client_secret => $ENV{CLIENT_SECRET}
, refresh_token => $refresh_token
, spreadsheet_id => '1hGNULaWpYwtnMDDPPkZT73zLGDUgv5blwJtK7hAiVIU'
);
my($content, $res);
my $title = 'My foobar sheet';
my $sheet = $gs->get_sheet(title => $title);
# create a sheet if does not exit
unless ($sheet) {
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => [
{
addSheet => {
properties => {
title => $title,
index => 0,
},
},
},
],
},
);
$sheet = $content->{replies}[0]{addSheet};
}
my $sheet_prop = $sheet->{properties};
# clear all cells
$gs->clear_sheet(sheet_id => $sheet_prop->{sheetId});
# import data
my #requests = ();
my $idx = 0;
my #rows = (
[qw(name age favorite)], # header
[qw(tarou 31 curry)],
[qw(jirou 18 gyoza)],
[qw(saburou 27 ramen)],
);
for my $row (#rows) {
push #requests, {
pasteData => {
coordinate => {
sheetId => $sheet_prop->{sheetId},
rowIndex => $idx++,
columnIndex => 0,
},
data => $gs->to_csv(#$row),
type => 'PASTE_NORMAL',
delimiter => ',',
},
};
}
# format a header row
push #requests, {
repeatCell => {
range => {
sheetId => $sheet_prop->{sheetId},
startRowIndex => 0,
endRowIndex => 1,
},
cell => {
userEnteredFormat => {
backgroundColor => {
red => 0.0,
green => 0.0,
blue => 0.0,
},
horizontalAlignment => 'CENTER',
textFormat => {
foregroundColor => {
red => 1.0,
green => 1.0,
blue => 1.0
},
bold => \1,
},
},
},
fields => 'userEnteredFormat(backgroundColor,textFormat,horizontalAlignment)',
},
};
($content, $res) = $gs->request(
POST => ':batchUpdate',
{
requests => \#requests,
},
);
exit;
#Google Sheets API, v4
# Scopes
# https://www.googleapis.com/auth/drive View and manage the files in your Google D# # i# rive
# https://www.googleapis.com/auth/drive.file View and manage Google Drive files and folders that you have opened or created with this app
# https://www.googleapis.com/auth/drive.readonly View the files in your Google Drive
# https://www.googleapis.com/auth/spreadsheets View and manage your spreadsheets in Google Drive
# https://www.googleapis.com/auth/spreadsheets.readonly View your Google Spreadsheets
My solution was a bit weird..i tried every solution i found on internet and nothing. Surprisely this worked: delete the credentials.json, refresh, vinculate your app in your account again. The new credentials.json file will have the refresh token. Backup this file somewhere.
Then keep using your app until the refresh token error comes again. Delete the crendetials.json file that now is only with an error message (this hapenned in my case), then paste you old credentials file in the folder, its done!
Its been 1 week since ive done this and had no more problems.
Adding access_type=offline to the authorisation Google authorisation URL did the trick for me. I am using Java and Spring framework.
This is the code that creates the client registration:
return CommonOAuth2Provider.GOOGLE
.getBuilder(client)
.scope("openid", "profile", "email", "https://www.googleapis.com/auth/gmail.send")
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth?access_type=offline")
.clientId(clientId)
.redirectUriTemplate("{baseUrl}/{action}/oauth2/code/{registrationId}")
.clientSecret(clientSecret)
.build();
The important part here is the authorization URI, to which ?access_type=offline is appended.