Can't make OAuth work in Drupal 6 / Services 3 - drupal

I'm working on a project and the client requested i use OAuth to protect some custom services I've built. I've got everything working perfectly using no authentication and session authentication, but using OAuth makes the whole thing fall apart. I haven't been able to find any really good step by step tutorials.
Basically I've got the Services 6.x-3.1 and OAuth 6.x-3.0-beta4 modules installed on D6. I created an oauth context using this documentation. Then I setup a dummy user because I only want to create one set of consumer credentials for all of my clients. I assigned the context to the user and to the service. I'm using http://mydomain/oauth/authorized as the callback url.
To test I connect using http://term.ie/oauth/example/client.php. I use the endpoint http://mydomain/oauth/request_token. I add the response tokens to the appropriate fields. Then I change the endpoint to http://mydomain/oauth/authorize. It takes me to a page where I have to click to allow the connection. I'm taken to the http://mydomain/oauth/authorized page where I get a success message.
Now I change the endpoint to http://mydomain/oauth/access_token and I get a response with a new token. According to these instructions, I replace the new access tokens and change the endpoint to http://mydomain/myserviceendpoint. It connects using a GET request and returns
401 Unauthorized: The consumer is not authorized to access this
service.
It feels like I'm missing something. There is only one context in the system and it's assigned to both the consumer and the service. The consumer key and secret are part of the final url along with the access token. I've looked at permissions and I can't find anything out of place. I even changed my service to just return TRUE for the hook_services_access to bypass permissions.
Any ideas?

First you must understand the difference between the two.
first the session authentication takes a user name and password and authenticates. Then it takes the user permissions from drupal so you need to authenticate in each step to go through your application that communicate with your drupal. Anonymous users can get what they want according to your permission in drupal.
While in Aouth authentication you create a user and add token to him and only the user who had the token communicate with the application according to the permission rule you set to him . In case of anonymous users they can't retrieve or get anything. The communication between drupal and the client within the created used with a certain token.
Finally: goto user page and create a user and give him the application name also token and in your application code give the token to application and authenticate with the newly created user.
You must check your permissions to the newly created user.
Check this link

Same problem like you... As there are not really good tutorials, sometimes its hard, so hope that this helps someone.
Solution:
You have to give oauth permisions to your resources in your endpoint definition, in my_module_default_services_endpoint(). Let's say you want to enable resource user via oauth. Normally you would have:
...
$endpoint->resources['user'] = array(
'operations' => array(
'ta_create' => array('enabled' => 1, ),
),
'actions' => array(
'login' => array('enabled' => 1, ),
'logout' => array('enabled' => 1, ),
'ta_register' => array('enabled' => 1, ),
),
);
...
So you have to authorize your resources like this ('user_info' is the permision required to access this resource):
...
$endpoint->resources['user'] = array(
'operations' => array(
'ta_create' => array('enabled' => 1, 'services_oauth' => array('credentials' => 'token', 'authorization' => 'user_info', ), ),
),
'actions' => array(
'login' => array('enabled' => 1, ),
'logout' => array('enabled' => 1, 'services_oauth' => array('credentials' => 'token', 'authorization' => 'user_info', ), ),
'ta_register' => array('enabled' => 1, ),
),
);
...
The ones you enable via 'services_oauth' will be only accessible via oauth. I'm wondering if you can mix oauth access services and normal oauth services.
How did I find the solution:
If you enable services debugging, you will notice the following PHP warning:
Notice: Undefined index: services_oauth en _services_oauth_authenticate_call() (lĂ­nea 26 de C:/.../services_oauth.inc).
If you go to _services_oauth_authenticate_call() in services_oauth.inc and print the stack trace (thanks to http://php.net/manual/en/function.debug-print-backtrace.php):
ob_start();
debug_print_backtrace();
$trace = ob_get_contents();
ob_end_clean();
watchdog('my_module', 'StackTrace: %st', array('%st' => $trace,), WATCHDOG_DEBUG);
Then you will see that *$method['endpoint']['services_oauth'];* is not set. So you have to set it in my_module_default_services_endpoint()

Your service endpoint is not to be used with the OAuth test client. Once you are authenticated, you pass your OAuth tokens to the service endpoint along with your method.

Related

How to fix 401 Unauthorized response WP Rest API

Im making a request to another wordpress site on our network as below.
//Send the request to update the submission post
$response = wp_remote_request( $this->urls->assign_url, array(
'headers' => array(
'Content-Type' => 'application/json; charset=utf-8',
'Authentication' => 'Basic '.base64_encode('somename:somepassword')
),
'body' => json_encode($array),
'method' => 'POST',
'data_format' => 'body'
)
);
Im making this request via ajax.
The callback function is being called and sends back data.
Im also logged into the remote site.
Im using a nonce and the user being authorised in the headers is a valid user.
All I keep getting back is:
body: "{"code":"rest_not_logged_in","message":"You are not currently logged in.","data":{"status":401}}"
Ive only just started getting this since I updated the remote wordpress version. It was working fine before that.
Any thoughts.
I believe in order to authenticate the way you want to, you need to use a plugin - the built-in authentication method is not ideal for offsite requests since it is cookie based.
https://developer.wordpress.org/rest-api/using-the-rest-api/authentication/#authentication-plugins

Wordpress get user info from same browser logged into site

I'm trying to use the Wordpress API wp_get_current_user(), however it's always returning the 0 user, with empty data. I am on a fresh install of Wordpress and I have just created my own theme and added an API hook.
I see lots of guides/info on grabbing data using Nonce from a separate client/computer, but I'm just trying to get the $user from the same browser that should be already logged in via the wordpress admin interface. I've verified that my browser has cookies set. My understanding of verification is that wp_get_current_user() should be able to use these cookies to verify my user and return data. .
Just to show I am logged into wordpress
This is my functions.php
located under wp-content/themes/myapi/functions/
add_action('rest_api_init', function () {
register_rest_route( 'api', 'test',array(
'methods' => 'GET',
'callback' => 'logged_in_wp',
));
});
function logged_in_wp($request){
if ( is_user_logged_in() ) {
return new WP_Error( 'me', 'me', array( 'status' => 200 ) );
}
return new WP_Error( 'not-logged in WP', 'not-logged in WP', array( 'status' => 400 ) );
}
?>
I'm using the following URL to access the data
http://localhost:8080/?rest_route=/api/test
I'm expecting it to return a me,me,200, instead, i'm only seeing the not-logged-in 400 error.
so what is the difference between localhost:8080 and localhost:8080?rest_route=/api/test that wordpress cannot figure out that I am logged in?
So, I'm guessing since nobody is answering and based on the readings I've done. What I'm asking for is impossible. It seems it is a security response by wordpress. You will need to authenticate even if the user is logged into Wordpress on the same domain/browser.
What do you use to test your request?
Using postman, you can insert useful parameters which will help you on authentication. Hence if you want to logged in using the WordPress Rest api, you must insert information of the current user properly in the section Authorization (Chose basic authentication and inside, fill the username and the password of an existing account (in this case Admin) and try it again.
Here is what i did for an exemple:
Sample image for the authorization which will soon help to know about the current user login
next using
$user_id = username_exists($username);
$user = get_user_meta($user_id);
$response['code'] = 200;
using "get_user_meta(wp_get_current_user()->ID, 'nickname', true);"
you can now determine the current user been logged.
Here in this sample if you make good use of the above information, you can create a good function "logged_in_wp()".
Here is my result on postman
I hope this will help you by the way

What is the correct format and sequence to acquire the correct token for LinkedIn API V2?

Our LinkedIn APP no longer works with the evolution of V2. I have tried a couple of times and failed to create the correct token. I am seeking help to create the correct authorization link to get the token. Error from the App is currently "Empty oauth2 access token"
I created a new LinkedIn app to replace our old one. I have tried to follow the instructions from LinkedIn and Microsoft but my efforts have produced the following error
My most recent attempt was:
https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id=78xaqf0ereiisy&redirect_uri=https://www.gachina.com&state=gachina&scope=r_emailaddress r_liteprofile w_member_social
I received: https://www.gachina.com/?code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&state=gachina
then I used the format shown here:
https://learn.microsoft.com/en-us/linkedin/shared/authentication/authorization-code-flow
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code={authorization_code_from_step2_response}&redirect_uri=hhttps%3A%2F%2Fdev.example.com%2Fauth%2Flinkedin%2Fcallback&client_id={your_client_id}&client_secret={your_client_secret}
with
https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code=AQS65Njp1F9-L-mKSrAJKZeQ-ij2OX7wboTc30-hrfQIwwJ0yfWd4FBqxLl-ZXHmL5HurKud4t9WcGeHB62EfPNcy3ddoqT1LztUHhR59iL-Q8f9WLrX03d9e3OCTmY-3vR8a_4ENeIN0GFpeLy7DKRDmuUNcQ82UwScqhApdtwzEBw-_Y0duYG87Lc1KQ&redirect_uri=https://www.gachina.com/auth/Linkedin/callback&client_id=78xaqf0ereiisy&client_secret={client_secret}
but I receive the following upon submitting the above link with our {client secret} in place
{"error":"invalid_redirect_uri","error_description":"Unable to retrieve access token: appid/redirect uri/code verifier does not match authorization code. Or authorization code expired. Or external member binding exists"}
I am doing all of this within minutes. So, I do not believe there is an expiration of code.
Can you help identify the error of steps or code to receive a Token?
This will return the access token
$params = array('grant_type' => 'authorization_code',
'client_id' => $this->api_key,
'client_secret' => $this->api_secret,
'code' => $_GET['code'],
'redirect_uri' => base_url().$this->redirect,
);
// Access Token request
$url = 'https://www.linkedin.com/oauth/v2/accessToken?' . http_build_query($params);
$data_len = strlen(http_build_query($params));
// Tell streams to make a POST request
$context = stream_context_create(
array('http' =>
array('method' => 'POST','header'=> 'Content-Length: 0'
)
)
);
// Retrieve access token information
$response = file_get_contents($url, false, $context);
$token = json_decode($response);
return $token->access_token;

Symfony FOSOAuthServerBundle get tokens programmatically?

Using the standard endpoint for FOSOAuthServerBundle (with FOSUserBundle), I can retrieve an access and refresh token by providing a client_id, client_secret, user and password combination. The response looks like this:
{
"accessToken": "YTg2ZTJkNTY2MGM5MGQyNzZjYjkyZWMwYzg1YTZmZTZmOTIyMzAxNDY2MTkwZDU5ODYzZTAzYmIyNDI0YTQ4ZQ",
"expiresIn": 3600,
"tokenType": "bearer",
"refreshToken": "OTU1MGZhNDQ2ODFkZDUzMmQ4Y2FhNTk5OWM0NWFlNDk0YTY0ZDZhOTRjZTUwM2JlYTE3MDkxYzU3ZWY1OGRkYQ"
}
My question is, how can I retrieve similar data programmatically by passing in the client and user credentials? I.e. How can I make the same call from another part of my application without going via HTTP (slow), but rather directly via the bundle code (fast)?
I'm sure there must be an easy way of doing this, but the best I can find so far is this https://github.com/FriendsOfSymfony/FOSOAuthServerBundle/issues/347 which doesn't really achieve the same thing as the HTTP call.
Here is how you can get the same response directly from the fos_oauth_server.server service using a request object:
$grantRequest = new Request(array(
'client_id' => $clientId,
'client_secret' => $clientSecret,
'grant_type' => 'password',
'username' => $username,
'password' => $password
));
$tokenResponse = $this->get('fos_oauth_server.server')->grantAccessToken($grantRequest);
$token = $tokenResponse->getContent();
My understanding is that you're using password grant type. This would require that your application knows a user and password pair to get a token. I would suggest instead to use client_credentials grant type.
Using the FOSOAuthServerBundle you should be able to get an access token with something like (in a ContainerAware context)
$this->get('fos_oauth_server.server')->grantAccessToken($request)
Here as you can see a Request object is required, but you can forge this object easily
In alternative you could try
$this->get('fos_oauth_server.server')->createAccessToken($client, null)
Where $client is an instance of you OAuth client.

How can I connect garb with a token from omniauth_google_oauth2?

I need to connect to Google Analytics. I'm using omniauth_google_oauth2 for authenticating the user with the app, and this gives me a token. The problem is that I need to connect to this user's Google Analytics account. To connect GA to my app I'm using the garb gem which has only two methods for authentication; username/password, and OAuth token. When I use the token provided by omniauth_google_oauth2, it doesn't work.
How do I create this new token using only the oauth_token that I get from the authentication with omniauth_google_oauth2?
I know I'm late to the party on this one but I solved a similar issue. You can't use omniauth_google_oauth2 with garb unless you use a fork of garb that supports oauth2. There is one here that is well maintained by Sija. However, you will need to use an oauth2 client object in order to create a session with this fork. You can get your user's profile set up using omniauth_google_oauth2 and make sure you save the refresh token for the user, then when you want to grab analytics data with garb, refresh the token with oauth2 and then pass that object into your garb session to pull the user's data. Here's an example after you have the refresh_token from omniauth stored somewhere:
client = OAuth2::Client.new YOURGOOGLEAPIKEY, YOURGOOGLEAPISECRET,
{
:site => 'https://accounts.google.com',
:authorize_url => "/o/oauth2/auth",
:token_url => "/o/oauth2/token",
}
response = OAuth2::AccessToken.from_hash(client, :refresh_token => omniauth_refresh_token).refresh!
Garb::Session.access_token = response
I think the problem you're encountering is that garb will only authenticate a user using OAuth 1 (or a username/password combo), while omniauth_google_oauth2 is (obviously) OAuth 2.
The only solution I've found is to use Google's deprecated OAuth 1 implementation as follows...
Gemfile:
gem 'omniauth-google', :git => 'git://github.com/davidkpham/omniauth-google.git'
# This fork relaxes dependencies on omniauth itself
Initializer (for Google Analytics access):
provider :google, 'XXXXXXXXXXXX.apps.googleusercontent.com', 'YOUR_SECRET', scope: 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile https://www.google.com/analytics/feeds/'
On the callback, store some of the stuff passed back:
auth = request.env["omniauth.auth"]
session[:google_token] = auth.credentials.token
session[:google_secret] = auth.credentials.secret
Then construct an AccessToken for garb:
if session[:google_token] and session[:google_secret]
consumer = OAuth::Consumer.new('XXXXXXXXXXXX.apps.googleusercontent.com', 'YOUR_SECRET', {
:site => 'https://www.google.com',
:request_token_path => '/accounts/OAuthGetRequestToken',
:access_token_path => '/accounts/OAuthGetAccessToken',
:authorize_path => '/accounts/OAuthAuthorizeToken'
})
garbsession = Garb::Session.new
garbsession.access_token = OAuth::AccessToken.new(consumer, session[:google_token], session[:google_secret])
# Once we have an OAuth::AccessToken constructed, do fun stuff with it
ga_id = "UA-XXXXXXX-X"
profile = Garb::Management::Profile.all(garbsession).detect {|p| p.web_property_id == ga_id}
ga_monthly = GoogleAnalyticsDate.results(profile, :start_date => (Date.today - 30), :end_date => Date.today, :sort => :date)
puts ga_monthly
end

Resources