Can't handle HTTP multiple attribute values in Perl - apache

I'm facing with a really strange issue. I interfaced a SAML authentication with OTRS which is an ITSM written in Perl and the Identity Provider sends the attributes as follow :
LoginName : dev-znuny02
mail : test2#company.dev
Profile : company.autre.idp.v2()
Profile : company.autre.mcf.sp(dev)
givenName : MyName
sn : Test2
I handle these with a module called Mod_Auth_Mellon and as you can see the attribute Profile is multivaluated. In short I retrieve all of these values with the following snippet :
sub new {
my ( $Type, %Param ) = #_;
# allocate new hash for object
my $Self = {};
bless( $Self, $Type );
$Self->{ConfigObject} = $Kernel::OM->Get('Kernel::Config');
$Self->{UserObject} = Kernel::System::User->new( %{$Self} );
# Handle header's attributes
$Self->{loginName} = 'MELLON_LoginName';
$Self->{eMail} = 'MELLON_mail';
$Self->{Profile_0} = 'MELLON_Profile_0';
$Self->{Profile_1} = 'MELLON_Profile_1';
$Self->{gName} = 'MELLON_givenName';
$Self->{sName} = 'MELLON_sn';
return $Self;
}
sub Auth {
my ( $Self, %Param ) = #_;
# get params
my $lname = $ENV{$Self->{loginName}};
my $email = $ENV{$Self->{eMail}};
my $profile0 = $ENV{$Self->{Profile_0}};
my $profile1 = $ENV{$Self->{Profile_1}};
my $gname = $ENV{$Self->{gName}};
my $sname = $ENV{$Self->{sName}};
...
}
I can handle all the values of the attributes except the attribute Profile. When I take a look to the documentation, they said :
If an attribute has multiple values, then they will be stored as MELLON_<name>_0, MELLON_<name>_1, MELLON_<name>_2
To be sure, I activated the diagnostics of the Mellon module and indeed I receive the information correctly :
...
MELLON_LoginName : dev_znuny02
MELLON_LoginName_0 : dev_znuny02
MELLON_mail : test2#company.dev
MELLON_mail_0 : test2#company.dev
MELLON_Profile : company.autre.idp.v2()
MELLON_Profile_0 : company.autre.idp.v2()
MELLON_Profile_1 : company.autre.mcf.sp(dev)
...
When I try to manipulate the MELLON_Profile_0 or MELLON_Profile_1 attributes in the Perl script, the variable assigned to it seems empty. Do you have any idea on what can be the issue here ?
Any help is welcome ! Thanks a lot guys
PS : I have no control on the Identity Provider so I can't edit the attributes sent

I didn't managed to make it work but I found a workaround to prevent users who don't have the Profile attribute value from logging into the application:
MellonCond Profile company.autre.mcf.sp(dev)
according the documentation :
You can also utilize SAML attributes to control whether Mellon authentication succeeds (a form of authorization). So even though the IdP may have successfully authenticated the user you can apply additional constraints via the MellonCond directive. The basic idea is that each MellonCond directive specifies one condition that either evaluates to True or False.

Related

Automatically determine values for Terraform Azure Private endpoint module

I need a help with a terraform module that I've created. It works perfectly, but I need to add some automation.
I created a module which creates multiple private endpoints, but I always need to put the variable values in manually.
This is the module:
resource "azurerm_private_endpoint" "endpoint" {
for_each = try({ for endpoint in var.endpoints : endpoint.name => endpoint }, toset([]))
name = each.key
location = var.location
resource_group_name = var.resource_group_name
subnet_id = each.value.subnet_id
dynamic "private_service_connection" {
for_each = each.value.private_service_connection
content {
name = each.key
private_connection_resource_id = private_service_connection.value.private_connection_resource_id
is_manual_connection = false
subresource_names = var.subresource_name ### see values on : https://learn.microsoft.com/fr-fr/azure/private-link/private-endpoint-overview#private-link-resource
}
}
lifecycle {
ignore_changes = [
private_dns_zone_group
]
}
tags = var.tags
}
I need to have:
1 - for the private endpoint name : I need it to be automatically provided: "pendp-(the subresource_name value in lower cases- my resource_name =>(mysql server for example))"
2 - for the private connection name: I need the values to be automatically: "connection-(the subresource_name value in lower cases- my ressource_name =>(mysql server for exemple))"
3 - some automation to detect automatically the subresource_name ( if I create a private endpoint for a blob or for a mariadb or for a mysqlserver, the module should detected it.
terraform version:
terraform {
required_version = "~> 1"
required_providers {
azurerm = "~> 3.0"
}
}
The easiest way to combine values automatically would be to use the Terraform string join() function to join multiple strings together. For lower case strings, you can use the lower() function.
Some examples:
name = join("-", ["pandp", lower(var.subresource_name)])
...
name = join("-", ["connection", lower(var.subresource_name), lower(each.key)])
For your third rule, you want to use a conditional expression to determine if it's a blob, or mariadb, or mysqlserver.
In this example, we set an example_name local with a value some-blob-value if var.subresource_name contains a string that starts with "blob", and set it to something-else if the condition is false:
locals {
example_name = startswith(lower(var.subresource_name), "blob") ? "some-blob-value" : "something-else"
}
There are many options available for doing a conditional on if a value is passed to what you expect and then determine a result based on that value. What exactly you want isn't clear in the question, but hopefully this will get you pointed in the right direction.
Terraform even has several helper functions that might help you if you only need part of a string, such as startswith(), endswith(), or contains() depending on your needs.

Writing to a config file dynamically using PHP

Implementing a SAAS (multi-tenant) app, I have a situation where an app would need to connect to different databases based on the user who wants to login. The databases are for separate institutions. Let's say MANAGER-A for institution-A, MANAGER-B for institution-B, want to login into their various institutions.
The process I'm implementing is this: There are 3 databases involved: DEFAULT-DB, INSTITUTION-A-DB, INSTITUTION-B-DB. The DEFAULT-DB contains all the login and database credentials of each user. This means that before MANAGER-A can login to his app, what happens is that, first, he will be logged in into the DEFAULT-DB, if successful, his details would be fetched and logged and set as the parameter of the config.php file. This means that connection will be dynamic based on the params fetched and passed by the DEFAULT-DB. My questions are these:
How do I write these params dynamically to the config.php file?
Secondly, I'm open to experts advise if my implementation is not the best.
Config.php file
<?php
unset($CFG);
global $CFG;
$CFG = new stdClass();
$CFG->dbtype = 'mysqli';
$CFG->dblibrary = 'native';
$CFG->dbhost = 'localhost';
$CFG->dbname = 'mydb';
$CFG->dbuser = 'root';
$CFG->dbpass = '';
$CFG->prefix = 'my_';
$CFG->dboptions = array (
'dbpersist' => 0,
'dbport' => '',
'dbsocket' => '1',
'dbcollation' => 'utf8mb4_unicode_ci',
);
$CFG->wwwroot = 'http://localhost:8888/myapp';
$CFG->dataroot = '/Applications/MAMP/data/myapp_data';
$CFG->admin = 'admin';
$CFG->directorypermissions = 0777;
require_once(dirname(__FILE__) . '/lib/setup.php');
This is Moodle. I have tried IOMAD, it is a great app but does not address my need.
That is a bad solution, IMHO. When you rewrite the configuration file, what happens when the next request comes in that loads that file? They will load the wrong configuration.
I would create two additional configuration files: config_inst_a.php and config_inst_b.php. Then set a session variable when the user logs in that contains the settings file name to load. You can then redefine the database information variables in the additional settings files. If the session variable has a filename in it, load that file AFTER the default config and the database connection values will be replaced.
Added sample code:
Really, really brief:
// Log in user here, and get info about what user company..
$session_start();
$_SESSION['User_ConfigFile'] = 'settings'.$userCompany.'.php';
// More code, or page redirect, or whatever, but the below goes on every page AFTER the default DB is included:
$session_start();
require_once($_SESSION['User_ConfigFile']);
If it helps somebody, my solution, not in production yet, but for now it works like a charm.
if ($_SERVER['SERVER_NAME'] == 'institutionA.mydomain.com') {
$CFG->dbname = 'institutionA'; // database name, eg moodle
$CFG->dbuser = 'user_institutionA'; // your database username
$CFG->wwwroot = 'https://institutionA.mydomain.com';
$CFG->dataroot = 'dataroot_institutionA';
//
$CFG->some_custom_data = 'my_institutiona_data';
} else
if ($_SERVER['SERVER_NAME'] == 'institutionB.mydomain.com') {
$CFG->dbname = 'institutionB'; // database name, eg moodle
$CFG->dbuser = 'user_institutionB'; // your database username
$CFG->wwwroot = 'https://institutionB.mydomain.com';
$CFG->dataroot = 'dataroot_institutionB';
//
$CFG->some_custom_data = 'my_institutionB_data';
} else {
...... anything you need in this case
}

Paypal Php Sdk - NotifyUrl is not a fully qualified URL Error

I have this code
$product_info = array();
if(isset($cms['site']['url_data']['product_id'])){
$product_info = $cms['class']['product']->get($cms['site']['url_data']['product_id']);
}
if(!isset($product_info['id'])){
/*
echo 'No product info.';
exit();
*/
header_url(SITE_URL.'?subpage=user_subscription#xl_xr_page_my%20account');
}
$fee = $product_info['yearly_price_end'] / 100 * $product_info['fee'];
$yearly_price_end = $product_info['yearly_price_end'] + $fee;
$fee = ($product_info['setup_price_end'] / 100) * $product_info['fee'];
$setup_price_end = $product_info['setup_price_end'] + $fee;
if(isset($_SESSION['discountcode_amount'])){
$setup_price_end = $setup_price_end - $_SESSION['discountcode_amount'];
unset($_SESSION['discountcode_amount']);
}
$error = false;
$plan_id = '';
$approvalUrl = '';
$ReturnUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=process_agreement';
$CancelUrl = SITE_URL.'payment/?payment_type=paypal&payment_page=cancel_agreement';
$now = $cms['date'];
$now->modify('+5 minutes');
$apiContext = new \PayPal\Rest\ApiContext(
new \PayPal\Auth\OAuthTokenCredential(
$cms['options']['plugin_paypal_clientid'], // ClientID
$cms['options']['plugin_paypal_clientsecret'] // ClientSecret
)
);
use PayPal\Api\ChargeModel;
use PayPal\Api\Currency;
use PayPal\Api\MerchantPreferences;
use PayPal\Api\PaymentDefinition;
use PayPal\Api\Plan;
use PayPal\Api\Patch;
use PayPal\Api\PatchRequest;
use PayPal\Common\PayPalModel;
use PayPal\Api\Agreement;
use PayPal\Api\Payer;
use PayPal\Api\ShippingAddress;
// Create a new instance of Plan object
$plan = new Plan();
// # Basic Information
// Fill up the basic information that is required for the plan
$plan->setName($product_info['name'])
->setDescription($product_info['desc_text'])
->setType('fixed');
// # Payment definitions for this billing plan.
$paymentDefinition = new PaymentDefinition();
// The possible values for such setters are mentioned in the setter method documentation.
// Just open the class file. e.g. lib/PayPal/Api/PaymentDefinition.php and look for setFrequency method.
// You should be able to see the acceptable values in the comments.
$setFrequency = 'Year';
//$setFrequency = 'Day';
$paymentDefinition->setName('Regular Payments')
->setType('REGULAR')
->setFrequency($setFrequency)
->setFrequencyInterval("1")
->setCycles("999")
->setAmount(new Currency(array('value' => $yearly_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
// Charge Models
$chargeModel = new ChargeModel();
$chargeModel->setType('SHIPPING')
->setAmount(new Currency(array('value' => 0, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$paymentDefinition->setChargeModels(array($chargeModel));
$merchantPreferences = new MerchantPreferences();
// ReturnURL and CancelURL are not required and used when creating billing agreement with payment_method as "credit_card".
// However, it is generally a good idea to set these values, in case you plan to create billing agreements which accepts "paypal" as payment_method.
// This will keep your plan compatible with both the possible scenarios on how it is being used in agreement.
$merchantPreferences->setReturnUrl($ReturnUrl)
->setCancelUrl($CancelUrl)
->setAutoBillAmount("yes")
->setInitialFailAmountAction("CONTINUE")
->setMaxFailAttempts("0")
->setSetupFee(new Currency(array('value' => $setup_price_end, 'currency' => $cms['session']['client']['currency']['iso_code'])));
$plan->setPaymentDefinitions(array($paymentDefinition));
$plan->setMerchantPreferences($merchantPreferences);
// ### Create Plan
try {
$output = $plan->create($apiContext);
} catch (Exception $ex){
die($ex);
}
echo $output->getId().'<br />';
echo $output.'<br />';
Been working with paypal php sdk for some days now and my code stop working.
So i went back to basic and i am still getting the same damn error.
I am trying to create a plan for subscription but getting the following error:
"NotifyUrl is not a fully qualified URL"
I have no idea how to fix this as i dont use NotfifyUrl in my code?
Could be really nice if anyone had an idea how to fix this problem :)
Thanks
PayPal did a update to their API last night which has caused problem within their SDK.
They are sending back null values in their responses.
I MUST stress the error is not on sending the request to PayPal, but on processing their response.
BUG Report : https://github.com/paypal/PayPal-PHP-SDK/issues/1151
Pull Request : https://github.com/paypal/PayPal-PHP-SDK/pull/1152
Hope this helps, but their current SDK is throwing exceptions.
Use below simple fix.
Replace below function in vendor\paypal\rest-api-sdk-php\lib\PayPal\Api\MerchantPreferences.php
public function setNotifyUrl($notify_url)
{
if(!empty($notify_url)){
UrlValidator::validate($notify_url, "NotifyUrl");
}
$this->notify_url = $notify_url;
return $this;
}
If you get the same error for return_url/cancel_url, add the if condition as above.
Note: This is not a permanent solution, you can use this until getting the update from PayPal.
From the GitHub repo for the PayPal PHP SDK, I see that the error you mentioned is thrown when MerchantPreferences is not given a valid NotifyUrl. I see you're setting the CancelUrl and ReturnUrl, but not the NotifyUrl. You may simply need to set that as well, i.e.:
$NotifyUrl = (some url goes here)
$obj->setNotifyUrl($NotifyUrl);
Reason behind it!
error comes from.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
line.
if (filter_var($url, FILTER_VALIDATE_URL) === false) {
throw new \InvalidArgumentException("$urlName is not a fully qualified URL");
}
FILTER_VALIDATE_URL: according to this php function.
INVALID URL: "http://cat_n.domain.net.in/"; // IT CONTAIN _ UNDERSCORE.
VALID URL: "http://cat-n.domain.net.in/"; it separated with - dash
here you can dump your url.
vendor\paypal\rest-api-sdk-php\lib\PayPal\Validation\UrlValidator.php
public static function validate($url, $urlName = null)
{
var_dump($url);
}
And then check this here: https://www.w3schools.com/PHP/phptryit.asp?filename=tryphp_func_validate_url
you can check here what character will reason for invalid.

401 Error "oauth_problem=nonce_used" Adding Products To Magento w/ Rest API

Getting a 401 status with "oauth_problem=nonce_used" message return when attempting to add products to Magento using the rest api. Oddly, the products are still get imported but it's really throwing me off because I'm not getting the product id's back in which to update the stock info.
Magento install is brand new (crucialwebhost installer) 1.7.0.2 and the code I'm using is pretty much copied and pasted from magento site...
$callbackUrl = '****';
$temporaryCredentialsRequestUrl = "*****/oauth/initiate?oauth_callback=".urlencode($callbackUrl);
$adminAuthorizationUrl = '*****/admin/oauth_authorize';
$accessTokenRequestUrl = '*****/oauth/token';
$apiUrl = '*****/api/rest';
$consumerKey = '*****';
$consumerSecret = '******';
try
{
$authType = ($_SESSION['state'] == 2) ? OAUTH_AUTH_TYPE_AUTHORIZATION : OAUTH_AUTH_TYPE_URI;
$oauthClient = new OAuth($consumerKey, $consumerSecret, OAUTH_SIG_METHOD_HMACSHA1, $authType);
$oauthClient->enableDebug();
if(!isset($_GET['oauth_token']) && !$_SESSION['state'])
{
$requestToken = $oauthClient->getRequestToken($temporaryCredentialsRequestUrl);
$_SESSION['secret'] = $requestToken['oauth_token_secret'];
$_SESSION['state'] = 1;
header('Location: '.$adminAuthorizationUrl.'?oauth_token='.$requestToken['oauth_token']);
exit;
} else if($_SESSION['state'] == 1)
{
$oauthClient->setToken($_GET['oauth_token'], $_SESSION['secret']);
$accessToken = $oauthClient->getAccessToken($accessTokenRequestUrl);
$_SESSION['state'] = 2;
$_SESSION['token'] = $accessToken['oauth_token'];
$_SESSION['secret'] = $accessToken['oauth_token_secret'];
header('Location: '.$callbackUrl);
exit;
} else
{
$oauthClient->setToken($_SESSION['token'], $_SESSION['secret']);
$resourceUrl = "$apiUrl/products";
$productData = json_encode(array(
'type_id' => 'simple',
'attribute_set_id' => 4,
'sku' => $local_product['sku'],
'weight' => 1,
'status' => 1,
'visibility' => 4,
'name' => $local_product['name'],
'description' => $local_product['description'],
'short_description' => $local_product['description'],
'price' => $local_product['price'],
'tax_class_id' => 0,
));
$headers = array('Content-Type' => 'application/json');
$oauthClient->fetch($resourceUrl, $productData, OAUTH_HTTP_METHOD_POST, $headers);
$respHeader = $oauthClient->getLastResponseHeaders();
}
} catch(OAuthException $e)
{
print_r($e);
}
}
session_destroy();
Exact error: {"messages":{"error":[{"code":401,"message":"oauth_problem=nonce_used"}]}}
In Mage_Api2_Model_Resource, about line 227, locate
$this->getResponse()->setHeader('Location', $newItemLocation);
and insert just after this:
$this->getResponse()->setHttpResponseCode(202);
Ref: Wikipedia "HTTP Location":
The HTTP Location header field is returned in responses from an HTTP
server under two circumstances:
To ask a web browser to load a different web page. In this
circumstance, the Location header should be sent with an HTTP status
code of 3xx.
To provide information about the location of a newly
created resource. In this circumstance, the Location header should
be sent with an HTTP status code of 201 or 202
I had exactly the same problem and spend weeks tracking down the problem. It seems to be a strange combination of Apache with PHP and Rewriting. In the end I created a clean installation and the problem was gone. I also tried to create a second installation where the problem could be observed but failed - the error appeared only in my production system, not in any of test installations...
I looked at this and from what I see in the code, it looks like OAuth register all your calls and if it find out that the exact same nonce was actually used with the exact same timestamp as some previous call, it will just discard it with this very specific oauth_problem=nonce_used error.
Code from app/code/core/Mage/Oauth/Model/Server.php
/**
* Validate nonce request data
*
* #param string $nonce Nonce string
* #param string|int $timestamp UNIX Timestamp
*/
protected function _validateNonce($nonce, $timestamp)
{
$timestamp = (int) $timestamp;
if ($timestamp <= 0 || $timestamp > (time() + self::TIME_DEVIATION)) {
$this->_throwException('', self::ERR_TIMESTAMP_REFUSED);
}
/** #var $nonceObj Mage_Oauth_Model_Nonce */
$nonceObj = Mage::getModel('oauth/nonce');
$nonceObj->load($nonce, 'nonce');
if ($nonceObj->getTimestamp() == $timestamp) {
$this->_throwException('', self::ERR_NONCE_USED);
}
$nonceObj->setNonce($nonce)
->setTimestamp($timestamp)
->save();
}
So I would say, when you do calls through Magento API in REST you should take extra care that each and every request you make have its own unique generated combinaison timestamp / nonce value.
Also see
oauth_nonce. A random value, uniquely generated by the application.
oauth_timestamp. A positive integer, expressed in the number of seconds since January 1, 1970 00:00:00 GMT.
And
nonce_used: The nonce-timestamp combination has already been used.
From this source : http://devdocs.magento.com/guides/v2.0/get-started/authentication/gs-authentication-oauth.html
I had the exact same problem and to solve it I looked at the mod_rewrite apache module and turned on logging for this module which is done by adding this to your apache httpd.conf file (this is for apache 2.4x , 2.2x needs to be done differently
<IfModule mod_rewrite.c>
LogLevel mod_rewrite.c:trace8
</IfModule>
The errors are then logged out to the apache standard error_log
When I looked at the rewrite here I could see that my post request was being rewritten twice, the first time it add the products to magento and the second time it failed to add the product again as the nonce was used, obviously.
I could see that the rewrite rule that was causing this in the .htaccess was the one
## workaround for HTTP authorization
## in CGI environment
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
I check my configuration and I was indeed running fast cgi php and I checked this by checking the value of Server API from a php info script. I had spent so long trying to solve this that I knowing the root cause I simply changed PHP from CGI php to an apache module and hey preto my post request now is only rewritten once and returns that all elusive 200 response code.
Work Around:
Use SOAP API.
Reason for not using it before:
SOAP API didn't provide ability to at custom product attributes or product quantity increment fields.
Fix:
Add any field you want to the product using the SOAP api by first creating an array of objects for them like this (last 4 lines of code below repeated for each field added):
$additionalAttrs = array();
$per_item = new stdClass();
$per_item->key = 'price_per_item';
$per_item->value = $local_product['price'];
$additionalAttrs['single_data'][] = $per_item;
And then adding it to your product array with the key "additional_attributes" like:
'additional_attributes' => $additionalAttrs,
I know this work around only helps people that were avoiding the SOAP API for the same reason I was but hopefully it helps some of you. That error we're seeing where it tries to add a product twice seems to be server configuration specific and very hard to track down.

Google task API authentication issue ruby

I am having the problem to authenticate a user for google tasks.
At first it authenticates the user and do things perfect. But in the second trip it throws an error.
Signet::AuthorizationError - Authorization failed. Server message:
{
"error" : "invalid_grant"
}:
following is the code:
def api_client code=""
#client ||= (begin
client = Google::APIClient.new
client.authorization.client_id = settings.credentials["client_id"]
client.authorization.client_secret = settings.credentials["client_secret"]
client.authorization.scope = settings.credentials["scope"]
client.authorization.access_token = "" #settings.credentials["access_token"]
client.authorization.redirect_uri = to('/callbackfunction')
client.authorization.code = code
client
end)
end
get '/callbackfunction' do
code = params[:code]
c = api_client code
c.authorization.fetch_access_token!
result = c.execute("tasks.tasklists.list",{"UserId"=>"me"})
unless result.response.status == 401
p "#{JSON.parse(result.body)}"
else
redirect ("/oauth2authorize")
end
end
get '/oauth2authorize' do
redirect api_client.authorization.authorization_uri.to_s, 303
end
What is the problem in performing the second request?
UPDATE:
This is the link and parameters to user consent.
https://accounts.google.com/o/oauth2/auth?
access_type=offline&
approval_prompt=force&
client_id=somevalue&
redirect_uri=http://localhost:4567/oauth2callback&
response_type=code&
scope=https://www.googleapis.com/auth/tasks
The problem is fixed.
Solution:
In the callbackfunction the tokens which are received through the code provided by the user consent are stored in the database.
Then in other functions just retrieve those tokens from the database and use to process whatever you want against the google task API.
get '/callbackfunction' do
code = params[:code]
c = api_client code
c.authorization.fetch_access_token!
# store the tokens in the database.
end
get '/tasklists' do
# Retrieve the codes from the database and create a client
result = client.execute("tasks.tasklists.list",{"UserId"=>"me"})
unless result.response.status == 401
p "#{JSON.parse(result.body)}"
else
redirect "/oauth2authorize"
end
end
I am using rails, and i store the token only inside DB.
then using a script i am setting up new client before calling execute, following is the code.
client = Google::APIClient.new(:application_name => 'my-app', :application_version => '1.0')
client.authorization.scope = 'https://www.googleapis.com/auth/analytics.readonly'
client.authorization.client_id = Settings.ga.app_key
client.authorization.client_secret = Settings.ga.app_secret
client.authorization.access_token = auth.token
client.authorization.refresh_token = true
client.authorization.update_token!({access_token: auth.token})
client.authorization.fetch_access_token!
if client.authorization.refresh_token && client.authorization.expired?
client.authorization.fetch_access_token!
end
puts "Getting accounts list..."
result = client.execute(:api_method => analytics.management.accounts.list)
puts " ===========> #{result.inspect}"
items = JSON.parse(result.response.body)['items']
But,it gives same error you are facing,
/signet-0.4.5/lib/signet/oauth_2/client.rb:875:in `fetch_access_token': Authorization failed. Server message: (Signet::AuthorizationError)
{
"error" : "invalid_grant"
}
from /signet-0.4.5/lib/signet/oauth_2/client.rb:888:in `fetch_access_token!'
Please suggest why it is not able to use the given token? I have used oauth2, so user is already authorized. Now i want to access the api and fetch the data...
===================UPDATE ===================
Ok, two issues were there,
Permission is to be added to devise.rb,
config.omniauth :google_oauth2, Settings.ga.app_key,Settings.ga.app_secret,{
access_type: "offline",
approval_prompt: "" ,
:scope => "userinfo.email, userinfo.profile, plus.me, analytics.readonly"
}
refresh_token must be passed to the API call, otherwise its not able to authorize.
I hope this helps to somebody, facing similar issue.