I have already written an application in a procedural way and am trying to move into into a Laravel framework. I'm having trouble with the SOAP exchange section as I am getting an ID value that authenticates the user but cannot access that value (as a cookie) later in the program to authenticate the search.
Here is my code so far:
<?php namespace App;
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
use Illuminate\Http\RedirectResponse;
class SoapController {
private $auth_response;
private $cookie;
private $search_client;
private $search_response;
public function soapExchange() {
// create SOAP client and add service details
SoapWrapper::add(function ($service) {
$service
->name('WoSAuthenticate')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
SoapWrapper::service('WoSAuthenticate', function($service) {
// call authenticate() method to get SID cookie
$auth_response = $service->call('authenticate', []);
$cookie = $auth_response->return;
// test for cookie return
// print($cookie);
});
// create SOAP client and add service details
$search_client = new SoapWrapper;
$search_client::add(function ($service) {
$service
->name('WoSSearch')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
if (isset($auth_response->return)) {
// if there is an SID returned then add it to the cookie attribute of the search client
$search_client->__setCookie('SID', $cookie);
} else {
// route to relevant view to display throttle error
return redirect('throttle');
}
}
}
I am successfully retrieving the response from the Web API call and getting a code to authenticate the user, saved as $cookie. However, I need then to create another SoapWrapper for performing the search and this needs the ID code attached by using the __setCookie method. If nothing is returned by the authenticate call then it redirects to an error message via throttle.blade.php elsewhere.
Surely there is a way to return a value created from a function so that it can be used elsewhere?
** EDIT **
Looked into employing SoapClient instead and including all operations within a single function. It all relates to a specific Web API anyway so I guess separation of concerns is not so much of an issue. FYI the new class I am trying is this:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
Maybe try $GLOBALS?
<?php
$GLOBALS[data] = "something";
function abc(){
echo $GLOBALS[data];
}
?>
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
class SoapController extends Controller {
public $resultSoapStatus;
public $resultSoapAuthority;
public function heySoap{
SoapWrapper::add(function ($service) ...
$data = [
'MerchantID' => $MerchantID,
'Amount' => $Amount,
'Description' => $Description,
'Email' => $Email,
'Mobile' => $Mobile,
'CallbackURL' => $CallbackURL
];
SoapWrapper::service('test', function ($service) use ($data) {
$resultSoap = $service->call('PaymentRequest', [$data]);
$this->resultSoapStatus = $resultSoap->Status;
$this->resultSoapAuthority = $resultSoap->Authority;
});
if($this->resultSoapStatus == 100 && strlen($this->resultSoapAuthority) == 36)
{
//Do Something
}
else
{
return Redirect::back();
}
}
}
Enjoy bro
Related
I'm building a webapp in Laravel which consumes multiple external REST API's, in which I have to authenticate myself and retrieve an access token before I'm able to do perform requests. I have built that like so:
ExampleAPIServiceProvider.php
class ExampleAPIServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(ExampleClient::class, function () {
return new ExampleClient(
accessToken: $this->getAccessToken()
);
});
}
private function getAccessToken(): ?AccessToken
{
return $this->accessToken ?? $this->requestAccessToken($clientId, $clientSecret);
}
}
This enables me to perform requests like so:
ExampleController.php
class ExampleController extends Controller
{
public function __construct(protected ExampleClient $client) { ... }
public function index()
{
$response = $this->client->getExamles();
}
}
I'm trying to consume another service, but this is a SOAP service. I know I can use the SoapClient(). If I understand correctly, with soap I first have to provide the url I'm trying to fetch data from and then do authentication. This is the example that the Soap Server I'm trying to consume provides:
try {
$soap = new SoapClient($webservice_url);
$res = $soap->Authenticate(array('accessKey' => $key));
if (!isset($res->AuthenticateResult)) exit();
$sess_id = $res->AuthenticateResult;
$xmlvar = new SoapVar('<ns1:xmlDoc>'.$xml.'</ns1:xmlDoc>', XSD_ANYXML);
$res = $soap->ProcessJournal(array('sessionID' => $sess_id, 'administrationID' => $admin_id, 'xmlDoc' => $xmlvar));
} catch (SoapFault $e) {
// throw exception...
}
I'd like to build a small wrapper client for this SoapClient and register that to the service provider as well. However I'm not sure how, as it seems that authentication happens after providing the webservice url. With the example from the Soap Server, it would mean that I'd have to provide the $sess_id and $administrationId each time I want to consume the service.
I think the result I'd like to have is to be able to call this from example the controller:
$this->soapClient->url($url)->ProcessJournal(...);
How do I go about doing registering it to the service provider? Or is there any other solution which would allow me to provide the authentication credentials only once?
Thanks in advance.
I'm developing a webpage with Laravel 8 and I have issues with fetching a patron details by id from Patreon API. Here is my use case.
I’ve added "Login with Patreon" option to my webpage, and it works well. When someone login with Patreon successfully, I store her/his Patreon id and set remember token to login the member automatically when she/he visits my page next time.
The first login process is fine. The problem occurs when my Patron visits my page next time. Because I want to check whether I received any payment before I let she/he see all content. That’s why I need to get my patron details from a middleware. To do that I tried:
fetch_user() returns my account details instead of logged-in user.
fetch_user() with the access token that returns from Patreon when
someone login, returns unauthorized.
fetch_member_details() doesn’t work with the id I passed, which is an
integer like 5484646 because it requires a very long string like
55153fds-f45fd5sfs-fds42ds, I don't know what it's.
fetch_page_of_members_from_campaign() and fetch_member_details()
together to get the proper ID, but it takes ages to get data, which
is unacceptable.
So, how can it be done?
https://further-reading.net/2020/06/getting-names-of-your-patreon-patrons-by-tier/
This might be useful. I believe, there is not a direct single API for this, but you can -
First fetch all campaigns/tiers data
And then fetch patrons for each campaign/tier
I like to answer my question for those who need some help.
First of all, I use the official PHP package by Patreon
I've created a middleware to check if the user should be authorized again. In order to prevent the same process every single time, I set timeout to users table and check if it still has time to expire. If it does, no need to do anything. Of course, this is my use case, but without that explanation, some parts of the code can be nonsense to you.
// App\Http\Middleware\AuthenticateMember.php
public function handle(Request $request, Closure $next)
{
if (!Auth::check()) {
return $next($request);
}
if (Carbon::parse(Auth::user()->timeout)->isFuture()) {
return $next($request);
}
$this->refreshCredentials();
return $next($request);
}
If "timeout" isn't in the future, refreshCredentials method will be called. This is a method, which will trigger binding AuthGatewayContract to the service container.
// App\Trait\Users.php
public function refreshCredentials()
{
$gateway = App::make('App\Services\AuthGatewaysContract');
$gateway->ensureUserStillAuthenticated();
}
public function handleUserRecord($user)
{
return User::updateOrCreate([
'email' => $user['email']
], $user);
}
public function attemptToLogin($user, $remember = true)
{
Auth::login($user, $remember);
event(new Registered($user));
}
This is how the binding works:
// App\Providers\AppServiceProvider.php
public function register()
{
$this->app->singleton(AuthGatewaysContract::class, function () {
$routeParts = explode('/', url()->current());
$gateway = array_pop($routeParts); // this is how I know which "Login with ..." button is clicked.
$isGateway = Gateway::where('name', $gateway)->first();
$gateway = $isGateway ? ucfirst($gateway) : ucfirst(Auth::user()->gateway->name);
$class = "\App\Services\AuthGateways\\$gateway";
return new $class();
});
}
So Patreon.php is active gateway now, and ensureUserStillAuthenticated can be called:
// App\Services\AuthGateways\Patreon.php
public function ensureUserStillAuthenticated()
{
$this->authenticate([
'access_token' => Auth::user()->access_token,
'refresh_token' => Auth::user()->refresh_token,
]);
}
private function authenticate($tokens)
{
$patron = $this->fetchUserFromGateway($tokens);
$user = $this->handleResponseData($patron, $tokens);
$user = $this->handleUserRecord($user);
return $this->attemptToLogin($user);
}
private function fetchUserFromGateway($tokens)
{
// This is the only function that communicate with Patreon-php package.
$api_client = new API($tokens['access_token']);
return $api_client->fetch_user();
}
private function handleResponseData($data, $tokens)
{
return [
'name' => $data['data']['attributes']['full_name'],
'email' => $data['data']['attributes']['email'],
'password' => Hash::make(Str::random(24)),
'role_id' => $this->assignRoleId($data),
'payment_id' => Payment::where('name', 'patreon')->first()->id,
'gateway_id' => Gateway::where('name', 'patreon')->first()->id,
'access_token' => $tokens['access_token'],
'refresh_token' => $tokens['refresh_token'],
'timeout' => Carbon::today()->addMonth()->toDateString()
];
}
I am trying to find the logged in user in my application using Auth but i get trying to get property of non-object which i understand clearly that it is returning null.
In my code below, an event triggers my webhook and post is sent to the address below. The function orderCreateWebhook triggers but that is where the error comes from..
The line $get_template = Order::where('id', Auth::user()->id);. Why is Auth returning null please? I am logged as well because i use auth in this same controller for another function which works fine.
Is it because it a webhook ?
Controller
public function registerOrderCreateWebhook(Request $request)
{
$shop = "feas.myshopify.com";
$token = "8f43d89a64e922d7d343c1173f6d";
$shopify = Shopify::setShopUrl($shop)->setAccessToken($token);
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json", ['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook',
'format' => 'json'
]
]);
}
public function orderCreateWebhook(Request $request)
{
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
In your function registerOrderCreateWebhook you appear to be making a request to shopify api and providing your webhook as the address which shopify will redirect the user to upon success. If this is correct, that request does not know about the user who generated the original request that made the api request since the request is coming from a completely different origin.
You would need to pass some key along with the url and then obtain the user within orderCreateWebhook. Something like:
Shopify::setShopUrl($shop)->setAccessToken($token)->post("admin/webhooks.json",
['webhook' =>
['topic' => 'orders/create',
'address' => 'https://larashop.domain.com/order-create-webhook/some-unique-key',
'format' => 'json'
]
]);
My suggestion would be to have a unique hash stored somewhere that relates back to the user in your system, perhaps a column in your users table. I wouldn't use the user_id for security reasons. So you would end up with something like:
//route
Route::get('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
//or
Route::post('/order-create-webhook/{uniqueKey}', 'YourController#orderCreateWebhook');
// depending on the request type used by api which calls this endpoint
// controller function
public function orderCreateWebhook($uniqueKey, Request $request)
{
$user = User::where('unique_key', $uniqueKey)->first();
$get_template = Order::where('id', Auth::user()->id);
$baseurl = "https://apps.domain.net/smsapi";
$query = "?key=7e3e4d4a6cfebc08eadc&to=number&msg=message&sender_id=Shopify";
$final_uri = $baseurl.$query;
$response = file_get_contents($final_uri);
header ("Content-Type:text/xml");
}
Is it because it a webhook ?
Yes, you can't use sessions in a webhook. It's the shopify server which is making the call. You should read the doc, it may exist a way to give an unique identifier in your call to shopify api and get it back in the webhook to find your user associated.
just use this to get authenticated user
use the facade in your class/Controller
use Illuminate\Support\Facades\Auth
public function getAuthUser(){
$user = Auth::user()
if(!is_null($user)
{
//user is authenticated
}
else
{
// no user
}
}
I have two cakePHP apps on 2 different servers. One app is required to get data from the first one; I have succeeded to put the Restful architecture in place but I failed to implement an authentication procedure to the requests the server sends. I need to authenticate to secure the data. I have looked around on the web but can't seem to get it working. Can anyone point me to a resource / tutorial that explains this in detail.
What I would ultimately need would be a way to authenticate my server every time it sends a request to the other server. Any help would be appreciated.
I finally got it to work after some research; indeed one of the solutions is OAuth. In case you are facing the same problem, I can advise you this Plugin made for CakePHP.
In details what I did was put the OAuth Plugin into my API Server and I used it like so for my restful controller:
class RestObjectController extends AppController {
public $components = array('RequestHandler', 'OAuth.OAuth');
public $layout = FALSE;
public function token() {
$this->autoRender = false;
try {
$this->OAuth->grantAccessToken();
} catch (OAuth2ServerException $e) {
$e->sendHttpResponse();
}
}
public function index() {
$objects = $this->Object->find('all');
$this->set(array(
'objects' => $objects,
'_serialize' => array('objects')
));
}
The function RestObject.token() is what I would call to get an Access token which will be used to give me access to the Resources in my controller. (Note that by declaring OAuth in my controller components, all the resources within my controller will need an access token to be accessible).
So on the client Server I would get an access token in the following way:
public function acquireAccessToken(){
$this->autoRender = FALSE;
App::uses('HttpSocket', 'Network/Http');
$link = API_SERVER."rest_objects/token";
$data = array(
'grant_type' => 'client_credentials',
'client_id' => 'xxxx',
'client_secret' => 'xxxx'
);
$response = $httpSocket->post($link, $data);
if($response->code == 200){
$data = json_decode($response->body, true);
return $data['access_token'];
}
return FALSE;
}
This assumes that you have clients already set up as explained in the Plugin Doc (replace xxxx by the real values for the client credentials). Once I have my access token, all I have to do is use it as follows:
public function test(){
$this->layout = FALSE;
App::uses('HttpSocket', 'Network/Http');
$httpSocket = new HttpSocket();
if($access_token = $this->acquireAccessToken()){
$link = API_SERVER."rest_objects.json"; //For the index as e.g.
$data = array('access_token' => $access_token);
$response = $httpSocket->get($link, $data);
}
}
And here you have it! So start by reading the Oauth Specification to understand the Protocol (in particular the Obtaining Authorization part), see which protocol (can be different from the one I used) applies and adapt to your case by using the Plugin
Tutorial Here
I am creating a twitter log in feature for my project, the oauth step where the user has granted permission for my app to use their data returns the user to the /twitter-auth route, this route in turn initiates this method:
public function auth() {
/* Oauth token */
$token = Input::get('oauth_token');
/* Verifier token */
$verifier = Input::get('oauth_verifier');
/* Request access token */
$accessToken = Twitter::oAuthAccessToken($token, $verifier);
/* Set the session variables from the acccess token above */
Session::set('user_id', $accessToken['user_id']);
Session::set('username', $accessToken['screen_name']);
Session::set('oauth_token', $accessToken['oauth_token']);
Session::set('oauth_token_secret', $accessToken['oauth_token_secret']);
/* Determine if the user already exists in the database, if he/she does, then
only update the user, otherwise, store a new user. Also pass an instance of the
accessToken as flash data in both instances. */
if( User::where('twitter_id', $accessToken['user_id'])->first() == null )
{
$newUser = array(
'username' => $accessToken['screen_name'],
'oauth_token' => $accessToken['oauth_token'],
'oauth_token_secret' => $accessToken['oauth_token_secret'],
'twitter_id' => $accessToken['user_id']
);
User::create( $newUser );
return Redirect::to('/');
}
else
{
$userToUpdate = User::where('twitter_id', Session::get('user_id'))->first();
$userToUpdate->username = $accessToken['screen_name'];
$userToUpdate->oauth_token = $accessToken['oauth_token'];
$userToUpdate->oauth_token_secret = $accessToken['oauth_token_secret'];
$userToUpdate->twitter_id = $accessToken['user_id'];
$userToUpdate->save();
return Redirect::to('/');
}
}
The user is saved/updated as necessary, but the user is not redirected to the home page. This happens with the redirect code both inside and outside of the IF statement. I was wondering if anyone could give me any clues as to why the redirect isn't working?
You are missing a return
your function in this case auth() is returning the Redirect object but is the function calling your auth() function is returning the result back to the controller?
Please make sure that in your controller, you return the Redirect class that is from auth() function.
Just tested your code and works :
let's say you have a UserController :
routes.php
Route::get('twitter-auth',array('as'=>'twitter-auth', 'uses'=>'UserController#twitterAuth'));
UserController
the user model class is just passed by dependency injection, to test this part also.
<?php
class UserController extends BaseController {
public function __construct(User $u){
$this->user = $u;
}
public function twitterAuth(){
return $this->user->auth();
}
}
User model :
I had to modify the code a little to fit my setup also
public function auth(){
/* Oauth token */
$token = Input::get('oauth_token');
/* Verifier token */
$verifier = Input::get('oauth_verifier');
/* Request access token */
//$accessToken = Twitter::oAuthAccessToken($token, $verifier);
//emulate the request of access Token
$accessToken = [
'user_id'=>'11',
'screen_name'=>'fewfewfew',
'oauth_token'=>'12312321',
'oauth_token_secret'=>'12312232323'
];
/* Set the session variables from the acccess token above */
Session::set('user_id', $accessToken['user_id']);
Session::set('username', $accessToken['screen_name']);
Session::set('oauth_token', $accessToken['oauth_token']);
Session::set('oauth_token_secret', $accessToken['oauth_token_secret']);
/* Determine if the user already exists in the database, if he/she does, then
only update the user, otherwise, store a new user. Also pass an instance of the
accessToken as flash data in both instances. */
if( User::where('twitter_id', $accessToken['user_id'])->first() == null )
{
$newUser = array(
'username' => $accessToken['screen_name'],
'oauth_token' => $accessToken['oauth_token'],
'oauth_token_secret' => $accessToken['oauth_token_secret'],
'twitter_id' => $accessToken['user_id']
);
User::create( $newUser );
return Redirect::to('/');
}
else
{
$userToUpdate = User::where('twitter_id', Session::get('user_id'))->first();
$userToUpdate->username = $accessToken['screen_name'];
$userToUpdate->oauth_token = $accessToken['oauth_token'];
$userToUpdate->oauth_token_secret = $accessToken['oauth_token_secret'];
$userToUpdate->twitter_id = $accessToken['user_id'];
$userToUpdate->save();
return Redirect::to('/');
}
}
Let me know if this is what you wanted
Returning a Redirect to execute it is only possible from routes, controller actions and filters. Otherwise you have to call send()
Redirect::to('login')->send();