Is Zend_Service_Twitter usable/updated? - php

I found that many functions thats documented in Zend_Service_Twitter seem to be no longer working, or maybe i did the wrong thing.
Zend_Debug::dump($twitter->account->verifyCredentials());
returned
object(Zend_Rest_Client_Result)#50 (2) {
["_sxml":protected] => object(SimpleXMLElement)#55 (2) {
["request"] => string(33) "/1/account/verify_credentials.xml"
["error"] => string(19) "Incorrect signature"
}
["_errstr":protected] => NULL
}

This might very well be the case, as Twitter has updated their oAuth Authentication. It also broke my own oAuth Library. The big difference I found was that they added a oauth_verifier, which I did not remember for seeing in the original oAuth documentation when I was building my own library.
This diagram on Twitter's Developer page helps explain.
http://a0.twimg.com/images/dev/oauth_diagram.png
I can't really help you with the rest of your problem as I don't use the Zend Framework myself, but I think your assumption that it needs an update is correct.

Related

How to verify JWT with JWK - PHP

I am working on Identity server and OpenId connect and since I need to do the implementation in a more dynamic manner and on multiple microservices in different languages, I am trying to understand the flow and doing the implementation with different stacks without depending on the client SDKs provided by the particular Identity server provider we are using. (in production, most likely, we will use some already built libs but my intention now is to grasp the concept of verification from ground up)
Now I am trying to simulate a case where we already have the access and id tokens and they are sent to a simple REST PHP function, and:
Do verification of JWT signature
Expiration check on the token
Validation of Scope & Audience
Pass username back to the frontend
(not relevant but I generated the access_token with Authorisation code flow -> PKCE)
This is my verification flow, I am using jose-php packages:
# public key
$components = array(
'kty' => 'RSA',
'e' => 'AQAB',
'n' => 'x9vNhcvSrxjsegZAAo4OEuo...'
);
$public_key= JOSE_JWK::decode($components);
$jwt_string = 'eyJ...'; // Access_token
$jws = JOSE_JWT::decode($jwt_string);
$result = $jws->verify($public_key, 'RS256');
However, this returns undefined for $result. I am debugging other parts of the PHP script, and I will share my result with everyone here once I find a fix but I think there is a better way (not with provider exclusive client SDKs) to do this flow and there is a high chance that I am missing something.
If anyone has a background with JWT token verification with PHP for identity server, It will be really great if you can share any better alternative or suggestions to do this here
Thank you in advance :)
This is an answer for anyone who seeks a simple verification middleware for jwks, might not be ideal for production!!! You are more than welcome to suggest a better solution :)
I switched to firebase/php-jwt as it is more convenient and straightforward to use and it was fairly easier to go quickly through its code and it does not return undefined anymore. Now the middleware code for validation looks like below:
$jwks = ['keys' => [[], []]; 
// JWK::parseKeySet($jwks) returns an associative array of **kid** to private
// key. Pass this as the second parameter to JWT::decode.
// Instead of RS256 use your own algo
// $data can return error so wrap it in try catch and do as you desire afterward
$data= (array) JWT::decode("YOUR_ACCESS_TOKEN", JWK::parseKeySet($jwks), ['RS256', 'RS256']);
For those who are willing to test a sample encoding and decoding process, feel free to use the private key and public key below: (Credit to firebase documentation with a bit of tweaking on my side to convert it to a simple Laravel controller)
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use \Firebase\JWT\JWT;
use \Firebase\JWT\JWK;
use Illuminate\Support\Facades\Http;
class JWTValidation extends Controller
{
    public function bundle(){
        
        $privateKey = <<<EOD
        -----BEGIN RSA PRIVATE KEY-----
        MIICXAIBAAKBgQC8kGa1pSjbSYZVebtTRBLxBz5H4i2p/llLCrEeQhta5kaQu/Rn
        vuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t0tyazyZ8JXw+KgXTxldMPEL9
        5+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4ehde/zUxo6UvS7UrBQIDAQAB
        AoGAb/MXV46XxCFRxNuB8LyAtmLDgi/xRnTAlMHjSACddwkyKem8//8eZtw9fzxz
        bWZ/1/doQOuHBGYZU8aDzzj59FZ78dyzNFoF91hbvZKkg+6wGyd/LrGVEB+Xre0J
        Nil0GReM2AHDNZUYRv+HYJPIOrB0CRczLQsgFJ8K6aAD6F0CQQDzbpjYdx10qgK1
        cP59UHiHjPZYC0loEsk7s+hUmT3QHerAQJMZWC11Qrn2N+ybwwNblDKv+s5qgMQ5
        5tNoQ9IfAkEAxkyffU6ythpg/H0Ixe1I2rd0GbF05biIzO/i77Det3n4YsJVlDck
        ZkcvY3SK2iRIL4c9yY6hlIhs+K9wXTtGWwJBAO9Dskl48mO7woPR9uD22jDpNSwe
        k90OMepTjzSvlhjbfuPN1IdhqvSJTDychRwn1kIJ7LQZgQ8fVz9OCFZ/6qMCQGOb
        qaGwHmUK6xzpUbbacnYrIM6nLSkXgOAwv7XXCojvY614ILTK3iXiLBOxPu5Eu13k
        eUz9sHyD6vkgZzjtxXECQAkp4Xerf5TGfQXGXhxIX52yH+N2LtujCdkQZjXAsGdm
        B2zNzvrlgRmgBrklMTrMYgm1NPcW+bRLGcwgW2PTvNM=
        -----END RSA PRIVATE KEY-----
        EOD;
        $publicKey = <<<EOD
        -----BEGIN PUBLIC KEY-----
        MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8kGa1pSjbSYZVebtTRBLxBz5H
        4i2p/llLCrEeQhta5kaQu/RnvuER4W8oDH3+3iuIYW4VQAzyqFpwuzjkDI+17t5t
        0tyazyZ8JXw+KgXTxldMPEL95+qVhgXvwtihXC1c5oGbRlEDvDF6Sa53rcFVsYJ4
        ehde/zUxo6UvS7UrBQIDAQAB
        -----END PUBLIC KEY-----
        EOD;
        
        $payload = array(
            "iss" => "example.org",
            "aud" => "example.com",
            "iat" => 1356999524,
            "nbf" => 1357000000
        );
        
        $jwt = JWT::encode($payload, $privateKey, 'RS256');
        //echo "Encode:\n" . print_r($jwt, true) . "\n";
        
        $decoded = JWT::decode($jwt, $publicKey, array('RS256'));
        
        /*
         NOTE: This will now be an object instead of an associative array. To get
         an associative array, you will need to cast it as such:
        */
        
        $decoded_array = (array) $decoded;
        return response()->json(['jwt' => $jwt, 'decoded' => $decoded]);
        //echo "Decode:\n" . print_r($decoded_array, true) . "\n";
    }
}
Now back to my first question again :)
In case that I validate the key with the help of this library as the first piece of the code, am I exposing any vulnerability? or will it be a time-consuming task in long run to maintain a custom verification flow like this?
I'm in the same process right now :).
The biggest downside now is that I have to decode the JWT first to extract the iss endpoint, then call the .well-known/openid-configuration endpoint, extra the correct key from there and validate the JWT again with the correct key information.
I'm doing it manually now, because I'm not aware of any libraries that support this.

Twilio Whitepages Pro Phone Reputation addon use with PHP

This is my first touch with Twilio Add-ons.
I want to use the addon "Whitepages Pro Phone Reputation" to return the relative results of a +ZZZZZZZ phone number. I have install the addon on the account (for the client id) and checked the use on Lookups on the control panel.
My PHP code is the following:
require_once(__DIR__ . '/includes/Twilio/autoload.php');
use Twilio\Rest\Client;
$client = new Client('XXXXXXX', 'YYYYYY');
$number = $client->lookups
->phoneNumbers("+ZZZZZZZ")
->fetch(
array("type" => "carrier", "AddOns" => "whitepages_pro_phone_rep")
);
var_dump($number->addOns['results']['whitepages_pro_phone_rep']);
But it returns the following:
array(5) { ["request_sid"]=> string(34) "XRcfbbe6fc969c3d20f5f0a95913a22cde" ["status"]=> string(6) "failed" ["message"]=> string(47) "Requirements to invoke AddOns have not been met" ["code"]=> int(61003) ["result"]=> array(0) { } }
What's the wrong with my code?
Twilio developer evangelist here.
You are receiving an error there, the code is 61003. You can always search for error codes for Twilio and there is normally an explanatory page. Here is the explanation for Error 61003.
Sadly, that explanation doesn't have many solutions!
To me, it looks as though your code is correct, but I wonder whether you have installed and enabled the Add-on in your account for lookups. Go here to install the Add-on, then make sure you have lookups selected.
Let me know if that helps at all.
I work for Whitepages and have some experience working within the Twilio environment. I've been the lead engineer for a couple of joint Twilio / Whitepages webinars and put together the app we used this year at Signal.
Have you tried testing it through a browser? Might be easy to see if it's the code or account / sub account. The mixing up the sid / token between a main account and a sub account has happened to me once.
https://lookups.twilio.com/v1/PhoneNumbers/+XXXXXXXXXXX/?AddOns=whitepages_pro_phone_rep
This will ask you for your account sid and auth token. Make sure you're getting a response there. Then at least we've narrowed it down.
Also when using the + Twilio expects E.169 formatted number numbers so make sure the country code is there.
Side note: It does look like you have "type" which should be "Type" for your carrier response but I don't think that will have an effect on AddOns.
Disclaimer: I've never worked in PHP.
We'll get it sorted out for you. Thanks for your support!
EDIT: Just noticed...would '$number->addOns' need to be '$number->add_ons'?
I detected the problem. ohhh I was so stupid!
I did my tests using my own phone number (Vodafone Greece) but the service did not supports Greek phone numbers.
I do a test with a number from U.S.A. and it works as expected!

Youtube API v3 missing "creation date" in request

I'm trying to make Youtube v3 Data API work on my website.
I shamelessly copied this code from google's code samples, and it is not working. The error message showed is this:
An client error occurred: All cacheable requests must have creation dates.
I previously had issues with API keys as I forgot almost everything about APIs in general and I just thought this sample would have been useful to remember things. I managed to generate the appropriate key and now I know for sure it isn't the real problem.
Sadly Google didn't find posts related to this issue, except two links to the actual Php Library that I implemented in my site to make everything work. By looking at it closely I noticed a developer comment that could be useful.
$rawDate = $resp->getResponseHeader('date');
$parsedDate = strtotime($rawDate);
if (empty($rawDate) || false == $parsedDate) {
// We can't default this to now, as that means future cache reads
// will always pass with the logic below, so we will require a
// date be injected if not supplied.
throw new Google_Exception("All cacheable requests must have creation dates.");
}
I can understand english pretty well but I really don't know what to do now.
I even tried to add some sort of date in the request in my code, but it isn't working (you can laugh):
// Call the search.list method to retrieve results matching the specified
// query term.
$searchResponse = $youtube->search->listSearch('id,snippet', array(
'q' => $_GET['q'],
'maxResults' => $_GET['maxResults'],
'date' => strtotime(),
));
An client error occurred: (list) unknown parameter: 'date'
Any tips? Thank you in advance
EDIT: I know, this PHP library is currently in beta, but there must be some workaround.
EDIT 2: I found a temporary work around. I inverted the logic gate of that 'if' in the Php Library and now it works. But I don't like doing this, and I won't mark this as solved. At least if you know the reason of the bug please explain it to me, I'm really interested.

Google Core Reporting Credential Identification

I've read up on the Core Reporting docs, I've gone through different tutorials, and none of this makes sense.
Just from the point of trying to replicate demos, I'm still lost. The simple, SIMPLE demo I'm working with asks for this 0Auth 2.0 info:
// $client->setClientId('insert_your_oauth2_client_id');
// $client->setClientSecret('insert_your_oauth2_client_secret');
// $client->setRedirectUri('insert_your_oauth2_redirect_uri');
// $client->setDeveloperKey('insert_your_developer_key');
The JSON file I downloaded from https://code.google.com/apis/console?api=analytics, supplies some of that info:
{
"web":{
"auth_uri":"https://accounts.google.com/o/oauth2/auth",
"token_uri":"https://accounts.google.com/o/oauth2/token",
"client_email":"*********#developer.gserviceaccount.com",
"redirect_uris":["https://www.**********.com/oauth2callback"],
"client_x509_cert_url":"https://www.googleapis.com/robot/v1/metadata/x509/**********#developer.gserviceaccount.com",
"client_id":"**********.apps.googleusercontent.com",
"auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs",
"javascript_origins":["https://www.**********.com"]
}
}
CliendId and RedirectUri, got it, no prob. ClientSecret and DeveloperKey? I don't even see those listed ANYWHERE. I assume it could mean the Public and Private keys that you download, but even then, I have no idea how to access the PKCS #12 file or whatever that is.
I am hopelessly lost and the google documentation just keeps pulling me around in circles and never mentions what these pieces are and how to get them.
I didn't do anything to fix this. It just started working one day. I assume it was some kind of bug with Google's API that they got around to fixing, but I never found any other instances of the bug in other forums or help sites.

How do I implement Direct Identity based OpenID authentication with Zend OpenID

I'm using the Zend framework and the openid selector from http://code.google.com/p/openid-selector/ - however I find I can't login using sites like Google and Yahoo as they use direct identity based login system whereby one is just redirected to a url as opposed to entering a unique url of their own for authentication.
I've checked out many options and hacks but none of them seem to work. How can i get this to work here btw - how is it implemented at stack overflow? I could really use all the help here guys..
Edit
Well the issue here is that from what I have noticed is that the Zend OpenID class doesn't support OpenID 2.0 the thing is that a typical open ID providor gives you a unique url such as your-name.openid-providor.com or openid-providor.com/your-name and the Zend OpenId class just parses through that url and then redirects you to the providor website where upon authentication you are redirected back.
In the case of Yahoo and google - you don't enter a unique url instead you are redirected to the providors login site and upon login and authentication you are redirected back - so basically whats happeining is that the zend_openID object when it parses to tell who the providor is it fails to tell from the general url itself. Like when you click on teh Google link it redirects you to https://www.google.com/accounts/o8/id
Its more an issue with the zend openid object here and there isn't any help on zend related forums - so I was wondering if someone had already hacked or had an alteration I could make to the class to accomplish this. Sorry if I'm missing something but I'm kinda new to this and programming with open ID and have just started to get my feet wet.
Thanks for the follow up - I did check into RPX a while back and they do have a php class but I wasnt able to check it out plus I really just want to for now get the code selector used as on stackoverflow to work with Yahoo and Google authentication. There has to be some kind of way to tweak the parsing which the Zend OpenID class uses as it runs a series of regular expression checks to make a discovery.
Little late to the game but I was able to get this working with some hacks I found around the interwebs.
First. Yahoo. To get Yahoo working all I had to do was change the JavaScript to use me.yahoo.com instead of just yahoo.com and it worked perfectly with the version of the Zend Framework I'm using. Unfortunately Google still wasn't, so some hacking was in order.
All of these changes go in Zend/OpenId/Consumer.php
First, in the _discovery method add the following on the series of preg_match checks that starts at around line 740.
} else if (preg_match('/<URI>([^<]+)<\/URI>/i', $response, $r)) {
$version = 2.0;
$server = $r[1];
I added this right before the return false; statement that's in the else {} block.
Second, in the _checkId method you'll need to add 3 new blocks (I haven't dug around enough to know what causes each of these three cases to be called, so I covered all to be on the safe side.
Inside the $version <= 2.0 block, you'll find an if/else if/else block. In the first if statement ($this->_session !== null) add this to the end:
if ($server == 'https://www.google.com/accounts/o8/ud') {
$this->_session->identity = 'http://specs.openid.net/auth/2.0/identifier_select';
$this->_session->claimed_id = 'http://specs.openid.net/auth/2.0/identifier_select';
}
In the else if (defined('SID') block add this to the end:
if ($server == 'https://www.google.com/accounts/o8/ud') {
$_SESSION['zend_openid']['identity'] = 'http://specs.openid.net/auth/2.0/identifier_select';
$_SESSION['zend_openid']['claimed_id'] = 'http://specs.openid.net/auth/2.0/identifier_select';
}
And then after the else block (so outside the if/else if/else block all together, but still inside the $version <= 2.0 block) add this:
if ($server == 'https://www.google.com/accounts/o8/ud') {
$params['openid.identity'] = 'http://specs.openid.net/auth/2.0/identifier_select';
$params['openid.claimed_id'] = 'http://specs.openid.net/auth/2.0/identifier_select';
}
Link to the bug in Zend Framework Issue Tracker
I need to use Google's OpenID stuff, and I tried Steven's code and couldn't get it to work as-is. I've made some modifications.
The _discovery change method is still the same:
Zend/OpenId/Consumer.php, line 765, add:
} else if (preg_match('/<URI>([^<]+)<\/URI>/i', $response, $r)) {
$version = 2.0;
$server = $r[1];
The rest is different, though:
Zend/OpenId/Consumer.php, line 859 (after making the above change), add:
if (stristr($server, 'https://www.google.com/') !== false) {
$id = 'http://specs.openid.net/auth/2.0/identifier_select';
$claimedId = 'http://specs.openid.net/auth/2.0/identifier_select';
}
This is right before:
$params['openid.identity'] = $id;
$params['openid.claimed_id'] = $claimedId;
And to get it to return the ID, once authorized:
Zend/Auth/Adapter/OpenId.php, line 278:
if(isset($_REQUEST['openid_identity']))
{
$this->_id = $_REQUEST['openid_identity'];
$id = $this->_id;
}
This is right before:
return new Zend_Auth_Result(
Zend_Auth_Result::SUCCESS,
$id,
array("Authentication successful"));
Note that I have not thoroughly tested this code. The code below is even more shakey.
I have spent more time and I've gotten it to work with my Google Apps domain with the following changes, in addition to the above:
Zend/OpenId/Consumer.php, line 734
$discovery_url = $id;
if(strpos($discovery_url, '/', strpos($discovery_url, '//')+2) !== false) {
$discovery_url = substr($discovery_url, 0, strpos($discovery_url, '/', strpos($discovery_url, '//')+2));
}
$discovery_url .= '/.well-known/host-meta';
$response = $this->_httpRequest($discovery_url, 'GET', array(), $status);
if ($status === 200 && is_string($response)) {
if (preg_match('/Link: <([^><]+)>/i', $response, $r)) {
$id = $r[1];
}
}
This is right after:
/* TODO: OpenID 2.0 (7.3) XRI and Yadis discovery */
I believe that was the only change I had to make. I'm pretty sure there's supposed to be some checking involved with the above for security reasons, but I haven't looked far enough into it to see what they would be.
Going over all the advice provided - I've decided to ditch using the zend_openid class [ sorry about that zend ] and instead I've switched to using JanRains OpenID library. Its taken a few hours to get it up and running with my project but atleast its working like a breeze. Had to make a lot of hacking and a bit of code spill over to get it working but its worth it.
I couldn't use any of Zend adapters with Zend-Auth to settle this new code library in as the library did the authentication on its own. SO I hacked and made a generic adapter that just returned a filled zend_result set to the Auth object thus I authenticate using my library and merely store the result in the Auth object pulling a bit of a fast one one the Zend-Auth object rather than have to rewrite my code again.
The library is available at http://openidenabled.com/php-openid/
Thanks for all the help guys.
I'm dealing with similar issues. I'm planning on using RPX now with Zend Framework. Maybe I'll write an adapter. Just to let you know.
Info: 'RPS now' provides an all-in-one interface and UI for user registration with
facebook
Google
Yahoo
mySpaceID
Windows LiveID
OpenID
aol
I'm pretty sure that Yahoo only works with OpenID 2.0. If you want to support Yahoo users, you're going to have to upgrade to a library with 2.0 support. That's going to be a matter of more than tweaking some parsing.
Did you check out the manual -- Zend_OpenId_Consumer basics? Check out 38.2.2 on that page and let me know if this helps, because it should.
Specifically, I don't know if Google offers OpenID. I know that Yahoo worked because I've tried it a while back.
Thanks for the information. I started by using JanRain's library, but I have problems with getting Simple Registration to work: I have not succeeded in getting any data that way. And there is no documentation on using Attribute Exchange. :(
So, I found and was trying Zend/OpenId, but had the same problem as you: no Yahoo!, Google and who knows what else support. Reading this, it seems I'll have to get back to JanRain; RPX is not an option in my case as it's a third party service.

Categories