I'm using Slim and bshaffer's OAuth2.0 library to build out an API. Currently I'm trying to extend the Pdo.php class included with the OAuth library to override a few functions. However, when I try to run my code, I'm getting an error saying:
PHP Fatal error: Interface
'OAuth2\Storage\AuthorizationCodeInterface' not found
/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/Pdo.php on line
21
Since that is the base class, and I don't want to modify it, I'm not sure how to address this issue.
Also, using the base Pdo class like so works fine, it's able to find the interfaces
$storage = new OAuth2\Storage\Pdo(array('dsn' => $dsn, 'username' => $user, 'password' => $pw));
Here is my index.php file
<?php
use League\OAuth2\Server\Storage\SessionInterface;
require '/***/root/vendor/Slim/Slim.php';
require_once('/***/root/vendor/bshaffer/oauth2-server-php/src/OAuth2/Autoloader.php');
require_once('/***/root/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/Pdo.php');
require_once('/***/root/custom_pdo.php');
OAuth2\Autoloader::register();
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
$app->post(
'/token',
function () {
$dsn = 'mysql:host='.MYSQL_HOST.';dbname=DB';
$user = MYSQL_USER;
$pw = MYSQL_PASS;
$storage = new custom_pdo(array('dsn' => $dsn, 'username' => $user, 'password' => $pw));
$server = new OAuth2\Server($storage);
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
$response = $server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
print_r($response);
}
);
$app->run();
And here is my custom_pdo.php class
<?php
namespace OAuth2\Storage;
use OAuth2\OpenID\Storage\UserClaimsInterface;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
require_once('/***/root/vendor/bshaffer/oauth2-server-php/src/OAuth2/Autoloader.php');
require_once('/***/root/vendor/bshaffer/oauth2-server-php/src/OAuth2/Storage/AuthorizationCodeInterface.php');
class custom_pdo extends Pdo {
}
Bshaffer Pdo class: https://github.com/bshaffer/oauth2-server-php/blob/develop/src/OAuth2/Storage/Pdo.php
It looks like it was a namespace issue. I wasn't calling my create object statement with the namespace, and that was causing the various issues I was running into. Here is the final code:
index.php
<?php
use League\OAuth2\Server\Storage\SessionInterface;
require '/***/root/vendor/autoload.php';
require "/***/root/custom_pdo.php";
$app = new \Slim\Slim();
$app->post(
'/token',
function () {
$dsn = 'mysql:host='.MYSQL_HOST.';dbname=DB';
$user = MYSQL_USER;
$pw = MYSQL_PASS;
$storage = new OAuth2\Storage\custom_pdo(array('dsn' => $dsn, 'username' => $user, 'password' => $pw));
$server = new OAuth2\Server($storage);
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
$response = $server->handleTokenRequest(OAuth2\Request::createFromGlobals())->send();
print_r($response);
}
);
$app->run();
custom.php
<?php
namespace OAuth2\Storage;
use OAuth2\OpenID\Storage\UserClaimsInterface;
use OAuth2\OpenID\Storage\AuthorizationCodeInterface as OpenIDAuthorizationCodeInterface;
class custom_pdo extends Pdo {
}
Related
I'm using the chadicus/slim-oauth2 collection for slimframework 3.
This is my code atm (running on Apache2):
<?php
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use \Slim\Middleware\HttpBasicAuthentication\PdoAuthenticator;
use Chadicus\Slim\OAuth2\Http\RequestBridge;
use Chadicus\Slim\OAuth2\Http\ResponseBridge;
use Chadicus\Slim\OAuth2\Middleware;
use OAuth2;
use OAuth2\GrantType;
use OAuth2\Storage;
use Slim;
require '../vendor/autoload.php';
define(KUNDEN,'kunden');
define(VERTRAEGE,'vertraege');
define(ADRESSE,'adresse');
$config['displayErrorDetails'] = true;
$config['addContentLengthHeader'] = false;
$config['db']['host'] = "localhost";
$config['db']['user'] = "vv";
$config['db']['pass'] = "vv";
$config['db']['dbname'] = "vv";
$storage = new Storage\Memory(
[
'client_credentials' => [
'administrator' => [
'client_id' => 'administrator',
'client_secret' => 'password',
'scope' => 'superUser',
],
'foo-client' => [
'client_id' => 'foo-client',
'client_secret' => 'p4ssw0rd',
'scope' => 'basicUser canViewFoos',
],
'bar-client' => [
'client_id' => 'foo-client',
'client_secret' => '!password1',
'scope' => 'basicUser',
],
],
]
);
$server = new OAuth2\Server(
$storage,
[
'access_lifetime' => 3600,
],
[
new GrantType\ClientCredentials($storage),
]
);
$app = new \Slim\App(["settings"=>$config]);
$authMiddleware = new Middleware\Authorization($server, $app->getContainer());
$container=$app->getContainer();
$container['db'] = function ($c) {
$db = $c['settings']['db'];
$pdo = new PDO("mysql:host=" . $db['host'] . ";dbname=" . $db['dbname'],
$db['user'], $db['pass']);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
return $pdo;
};
$app->post('/token', function ($psrRequest, $psrResponse, array $args) use ($app, $server) {
//create an \OAuth2\Request from the current \Slim\Http\Request Object
$oauth2Request = RequestBridge::toOAuth2($psrRequest);
//Allow the oauth2 server instance to handle the oauth2 request
$oauth2Response = $server->handleTokenRequest($oauth2Request);
//Map the oauth2 response into the slim response
//print_r($server['storage']);
return ResponseBridge::fromOAuth2($oauth2Response);
});
$app->get('/'.KUNDEN, function (Request $request, Response $response) {
$query=$this->db->prepare("Select * from customer");
$query->execute();
return $response->withJson($query->fetchAll());
})->add($authMiddleware);
$app->run();
If i now request access to /token with postman and the administrator/password credentials, I get a token back. But if I try to open /kunden with this token I get: "Invalid token".
Get token back from server
Invalid token
I'm not sure if the token were stored in the memory correctly. And, to be honest, I have not many experience with oAuth2.
Can anyone give me a push in the right direction. I need a hint, where I have to search on the internet. Because "Slimframework oauth2 invalid token" are not the right keywords for google :-/
Thanks in advance!
Franz
I had the same problem and it was that i just didn't know how to work with OAuth2 Server PHP.
You must create the OAuth2 tables on your database as you can read here: https://bshaffer.github.io/oauth2-server-php-docs/cookbook/
Then you can create your users in the database and use PDO to authenticate and save the user token:
$pdo = new PDO("mysql:host=$dbhost;dbname=$dbname", $dbuser, $dbpass);
$storage = new Storage\Pdo($pdo);
$server = new OAuth2\Server(
$storage,
[
'access_lifetime' => 3600,
],
[
new GrantType\ClientCredentials($storage),
new GrantType\AuthorizationCode($storage),
]
);
For anyone else who stumbles upon this question from search engines.
First, you must understand that the "Memory" storage in OAuth2 is not persistent, meaning that when you create a token, it will not be saved anywhere. It's meant to show you that the token creation part of the mechanism is working.
Second, you must use another storage (PDO or Redis) if you want to authenticate clients to your routes. This is fairly simple to do with Redis. You just install redis-server on your server, secure it, and then implement it in your code. For an easy-to-use implementation of Redis, check the composer package predis/predis.
For more information how to implement OAuth2 with Redis, visit: https://bshaffer.github.io/oauth2-server-php-docs/storage/redis/
Have a nice day :)
I have a Laravel 4.2 app that I have successfully implemented Authorization Code using oauth2-server-php. However, I can't seem to get User Credential grants working.
Here's my code setting up the oauth server:
App::singleton(
'oauth2',
function () {
$host = Config::get('database.connections.mongodb.host');
$hosts = is_array($host) ? $host : [$host];
$dbName = Config::get('database.connections.mongodb.database');
$dbOptions =
empty( Config::get('database.connections.mongodb.options') ) ? [] : Config::get(
'database.connections.mongodb.options'
);
$mongo = new MongoClient('mongodb://' . implode(',', $hosts) . '/' . $dbName, $dbOptions);
$storage = new OAuth2\Storage\Mongo($mongo->{$dbName});
$server = new OAuth2\Server(
$storage, [
'always_issue_new_refresh_token' => true,
'refresh_token_lifetime' => 2419200,
]
);
$userStorage = new \Medusa\Oauth\Storage\MedusaUserCredentials();
$server->addStorage($userStorage, 'users');
$userCredentialGrant = new Oauth2\GrantType\UserCredentials($userStorage);
$server->addGrantType(new OAuth2\GrantType\AuthorizationCode($storage));
$server->addGrantType(new OAuth2\GrantType\ClientCredentials($storage));
$server->addGrantType($userCredentialGrant);
$server->addGrantType(new OAuth2\GrantType\RefreshToken($storage));
return $server;
}
);
MedusaUserCredentials has the following code:
namespace Medusa\Oauth\Storage;
use OAuth2\Storage\UserCredentialsInterface;
class MedusaUserCredentials implements UserCredentialsInterface
{
public function checkUserCredentials($username, $password)
{
return Auth::attempt(['email_address' => strtolower($username), 'password' => $password, 'active' => 1]);
}
public function getUserDetails($username)
{
return ['user_id' => $username];
}
}
When I post to the web server to the token route using a raw payload of
grant_type=password&username=<USERNAME>&password=<PASSWORD>
I just get the login page. The token route looks like this
Route::post(
'oauth/token',
function () {
$bridgedRequest = OAuth2\HttpFoundationBridge\Request::createFromRequest(Request::instance());
$bridgedResponse = new OAuth2\HttpFoundationBridge\Response();
$bridgedResponse = App::make('oauth2')->handleTokenRequest($bridgedRequest, $bridgedResponse);
print_r($bridgedResponse); die();
return $bridgedResponse;
}
);
What am I missing?
Thanks!
I found the issue -- I had a namespace issue that I had to resolve. For some reason, my app returned a 200 OK response and the normal login page, so I didn't think to check the logs.
I know, bad dev, no cookie!
I'm trying to use guzzle 6 which works fine but I'm lost when it comes to how to log all the api calls. I would like to simply log timing, logged in user from session, url and any other usual pertinent info that has to do with the API call. I can't seem to find any documentation for Guzzle 6 that refers to this, only guzzle 3 (Where they've changed the logging addSubscriber call). This is how my current API calls are:
$client = new GuzzleHttp\Client(['defaults' => ['verify' => false]]);
$res = $client->get($this->url . '/api/details', ['form_params' => ['file' => $file_id]]);
You can use any logger which implements PSR-3 interface with Guzzle 6
I used Monolog as logger and builtin middleware of Guzzle with MessageFormatter in below example.
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
$stack = HandlerStack::create();
$stack->push(
Middleware::log(
new Logger('Logger'),
new MessageFormatter('{req_body} - {res_body}')
)
);
$client = new \GuzzleHttp\Client(
[
'base_uri' => 'http://httpbin.org',
'handler' => $stack,
]
);
echo (string) $client->get('ip')->getBody();
The details about the log middleware and message formatter has not well documented yet. But you can check the list which variables you can use in MessageFormatter
Also there is a guzzle-logmiddleware which allows you to customize formatter etc.
#KingKongFrog This is the way to specify the name of the log file
$logger = new Logger('MyLog');
$logger->pushHandler(new StreamHandler(__DIR__ . '/test.log'), Logger::DEBUG);
$stack->push(Middleware::log(
$logger,
new MessageFormatter('{req_body} - {res_body}')
));
For Guzzle 7 I did this::
require './guzzle_7.2.0.0/vendor/autoload.php';
require './monolog/vendor/autoload.php';
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Pool;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\MessageFormatter;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use GuzzleHttp\TransferStats;
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$logger = null;
$messageFormat =
//['REQUEST: ', 'METHOD: {method}', 'URL: {uri}', 'HTTP/{version}', 'HEADERS: {req_headers}', 'Payload: {req_body}', 'RESPONSE: ', 'STATUS: {code}', 'BODY: {res_body}'];
'REQUEST: urldecode(req_body)';
$handlerStack = \GuzzleHttp\HandlerStack::create();
$handlerStack->push(createGuzzleLoggingMiddleware($messageFormat));
function getLogger() {
global $logger;
if ($logger==null) {
$logger = new Logger('api-consumer');
$logger->pushHandler(new \Monolog\Handler\RotatingFileHandler('./TataAigHealthErrorMiddlewarelog.txt'));
}
var_dump($logger);
return $logger;
}
function createGuzzleLoggingMiddleware(string $messageFormat){
return \GuzzleHttp\Middleware::log(getLogger(), new \GuzzleHttp\MessageFormatter($messageFormat));
}
function createLoggingHandlerStack(array $messageFormats){
global $logger;
$stack = \GuzzleHttp\HandlerStack::create();
var_dump($logger);
collect($messageFormats)->each(function ($messageFormat) use ($stack) {
// We'll use unshift instead of push, to add the middleware to the bottom of the stack, not the top
$stack->unshift(createGuzzleLoggingMiddleware($messageFormat) );
});
return $stack;
}
//$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
$client = new Client(['verify' => false, 'handler' => $tapMiddleware($handlerStack)]);
WOW !!
unshift() is indeed better than push() in reverse order ...
$handlers = HandlerStack::create();
$logger = new Logger('Logger');
$templates = [
'{code} >> {req_headers}',
'{code} >> {req_body}',
'{code} << {res_headers}',
'{code} << {res_body}'
];
foreach ($templates as $template) {
$handlers->unshift($this->getMiddleware($logger, $template));
}
$client = new Client([
RequestOptions::DEBUG => false,
'handler' => $handlers
]);
Using this function to obtain the Middleware:
private function getMiddleware(Logger $logger, string $template): callable {
return Middleware::log($logger, new MessageFormatter($template));
}
Logger comes from "monolog/monolog": "^1.27.1".
And these are all supported variable substitutions.
I am trying to run the example code here
But I am getting this error:
Payum\Core\Exception\InvalidArgumentException: A token with hash `RVpxpP1m3HnTWcj2oL19SQ38NWvCDIz5qeUwfr283kY` could not be found. in /var/www/test/vendor/payum/core/Payum/Core/Security/PlainHttpRequestVerifier.php on line 47
My code looks like this:
namespace Paypal\Model;
use Payum\Core\Model\ArrayObject;
class AgreementDetails extends ArrayObject {
}
namespace Paypal\Model;
use Payum\Core\Model\Token;
class PaymentSecurityToken extends Token
{
}
namespace Paypal\Model;
use Payum\Core\Model\ArrayObject;
class RecurringPaymentDetails extends ArrayObject{
}
config.php
use Buzz\Client\Curl;
use Payum\Paypal\ExpressCheckout\Nvp\PaymentFactory;
use Payum\Paypal\ExpressCheckout\Nvp\Api;
use Payum\Core\Registry\SimpleRegistry;
use Payum\Core\Storage\FilesystemStorage;
use Payum\Core\Security\PlainHttpRequestVerifier;
use Payum\Core\Security\GenericTokenFactory;
$tokenStorage = new FilesystemStorage('/home/vagrant/tmp', 'Paypal\Model\PaymentSecurityToken');
$requestVerifier = new PlainHttpRequestVerifier($tokenStorage);
$agreementDetailsClass = 'Paypal\Model\AgreementDetails';
$recurringPaymentDetailsClass = 'Paypal\Model\RecurringPaymentDetails';
$storages = array(
'paypal' => array(
$agreementDetailsClass => new FilesystemStorage('/home/vagrant/tmp',$agreementDetailsClass),
$recurringPaymentDetailsClass => new FilesystemStorage('/home/vagrant/tmp',$recurringPaymentDetailsClass)
)
);
$payments = array(
'paypal' => PaymentFactory::create(new Api(new Curl, array(
'username' => 'REPLACE WITH YOURS',
'password' => 'REPLACE WITH YOURS',
'signature' => 'REPLACE WITH YOURS',
'sandbox' => true
)
)));
$registry = new SimpleRegistry($payments, $storages, null, null);
$tokenFactory = new GenericTokenFactory(
$tokenStorage,
$registry,
'https://'.$_SERVER['HTTP_HOST'],
'capture.php',
'notify.php'
);
prepare.php
use Payum\Paypal\ExpressCheckout\Nvp\Api;
include 'config.php';
$storage = $registry->getStorageForClass($agreementDetailsClass, 'paypal');
$agreementDetails = $storage->createModel();
$agreementDetails['PAYMENTREQUEST_0_AMT'] = 0;
$agreementDetails['L_BILLINGTYPE0'] = Api::BILLINGTYPE_RECURRING_PAYMENTS;
$agreementDetails['L_BILLINGAGREEMENTDESCRIPTION0'] = $subscription['description'];
$agreementDetails['NOSHIPPING'] = 1;
$storage->updateModel($agreementDetails);
$captureToken = $tokenFactory->createCaptureToken('paypal', $agreementDetails, 'create_recurring_payment.php');
$agreementDetails['RETURNURL'] = $captureToken->getTargetUrl();
$agreementDetails['CANCELURL'] = $captureToken->getTargetUrl();
$storage->updateModel($agreementDetails);
header("Location: ".$captureToken->getTargetUrl());
capture.php
use Payum\Core\Request\BinaryMaskStatusRequest;
use Payum\Core\Request\SecuredCaptureRequest;
use Payum\Core\Request\RedirectUrlInteractiveRequest;
include 'config.php';
$token = $requestVerifier->verify($_REQUEST);
$payment = $registry->getPayment($token->getPaymentName());
$payment->execute($status = new BinaryMaskStatusRequest($token));
if (false == $status->isNew()) {
header('HTTP/1.1 400 Bad Request', true, 400);
exit;
}
if ($interactiveRequest = $payment->execute(new SecuredCaptureRequest($token), true)) {
if ($interactiveRequest instanceof RedirectUrlInteractiveRequest) {
header("Location: ".$interactiveRequest->getUrl());
die();
}
throw new \LogicException('Unsupported interactive request', null, $interactiveRequest);
}
$requestVerifier->invalidate($token);
header("Location: ".$token->getAfterUrl());
create_recurring_payment.php
same as here
I have confirmed that file storage class is able to write data to files, but on capture step it fails to verify the token.
Any sort of help is appreciated to get this code running.
Token storage is not configured correctly (not your fault the doc is wrong too). It has to use hash model field as id. Try:
<?php
$tokenStorage = new FilesystemStorage('/home/vagrant/tmp', 'Paypal\Model\PaymentSecurityToken', 'hash');
About the exception you've gotten. It tries to find token by id and uses for that token's hash. Ofcouce it is could not be found.
Here is my core php code:
require('common/Client.php'); // include php wrapper class
require('common/GrantType/IGrantType.php');// include php wrapper class//
require('common/GrantType/AuthorizationCode.php'); // include php wrapper class//
const CLIENT_ID = '***********'; //generated from base_camp api//
const CLIENT_SECRET ='***********';
const REDIRECT_URI = '***********';
const AUTHORIZATION_ENDPOINT = 'https://launchpad.37signals.com/authorization/new';
const TOKEN_ENDPOINT = 'https://launchpad.37signals.com/authorization/token';
session_start();
$client = new OAuth2\my_class(CLIENT_ID, CLIENT_SECRET);
if (!isset($_GET['code']))
{
$_SESSION['org'] = $_GET['org'];
$auth_url = $client->getAuthenticationUrl(AUTHORIZATION_ENDPOINT, REDIRECT_URI);
header('Location: ' . $auth_url);
die('Redirect');
}
else
{
$params = array( 'type' => 'web_server', 'client_id' => CLIENT_ID, 'redirect_uri' => REDIRECT_URI, 'client_secret' => CLIENT_SECRET, 'code' => $_GET['code']);
$response = $client->getAccessToken(TOKEN_ENDPOINT, 'authorization_code', $params);
$client->setAccessToken($response['result']['access_token']);
$org = $_SESSION['org'].'_ess';
mysql_connect('localhost','root','*******') or die('Cannot connect to database !');
mysql_select_db($org) or die('No database found in mysql !');
$gcntct = mysql_query("select * from e_users");
}
How do I initialize object of another class in mvc, I have adopt new method by initialize new by namespace name and class its show non existing class error.
oath2 is namespace and client is library class name.
function a()
{
$this->library('client');
How do pass clientid and secret key in during object creation codeigniter as if you would see my core php code I have initialize object with new operator and pass values , so how could we initialize object of class client and namespace with passing values in constructor.
}
client.php lib
namesapce oath2
class client
{
public function __construct($client_id, $client_secret, $client_auth = self::AUTH_TYPE_URI, $certificate_file = null)
{
if (!extension_loaded('curl')) {
throw new Exception('The PHP extention curl must be installed to use this library.', Exception::CURL_NOT_FOUND);
}
$this->client_id = $client_id;
$this->client_secret = $client_secret;
$this->client_auth = $client_auth;
$this->certificate_file = $certificate_file;
if (!empty($this->certificate_file) && !is_file($this->certificate_file)) {
throw new InvalidArgumentException('The certificate file was not found', InvalidArgumentException::CERTIFICATE_NOT_FOUND);
}
}
}
Create a library in Codeigniter and load it.
See here and here