Problems with session variables in slim php framework - php

I developed an API that I already have on a server. During the whole development I did tests with POSTMA and I had no problem, however, now that I want to use the API from the production domain to another domain it doesn't work: /
Production -> Local consumption POSTMAN: If it works
Production -> Local consumption from a website: Does not work
The problem is with the session variables, I have some consulates that occupy parameters such as "user ID" or "company id" that I set in the first step when I authenticate with JWT.
Step 1: Authentication (Route)
use App\Lib\Auth,
App\Validation\EmpleadoValidation,
App\Lib\Response;
$app->group('/autenticar/', function () {
$this->post('empresa', function ($req, $res, $args) {
$parametros = $req->getParsedBody();
if(isset($parametros['API']) && isset($parametros['API-KEY'])){
return $res->withHeader('Content-type', 'application/json')
->write(
json_encode($this->model->empresaAuth->autenticar($parametros['API'], $parametros['API-KEY']))
);
}else{
$r = new Response;
return $res->withHeader('Content-type', 'application/json')->withStatus(403)->write(json_encode($r->SetResponse(false, "Cabeceras no válidas")));
}
});
});
Step 1.2: Authenticate (Model)
public function autenticar($TOKEN, $KEY){
try{
$sql="SELECT e.id AS id_empresa, u.id AS id_admin, e.TOKEN, e.KEY,
u.email, e.nom_comercial
FROM empresas AS e
INNER JOIN empresas_usuariosprincipales AS u ON u.id_empresa=e.id
WHERE e.TOKEN = :TOKEN AND e.KEY= :KEY and e.API=1";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(":TOKEN", $TOKEN, PDO::PARAM_STR);
$stmt->bindParam(":KEY", $KEY, PDO::PARAM_STR);
$respuesta = $stmt ->execute();
$respuesta = $stmt ->fetch();
$data = $respuesta;
}catch(Exception $e){
die($e);
}
if(is_object($data)){
$token = Auth::SignIn([
'DISS' => $data->id_empresa.'-'.$data->id_admin,
'TOKEN' => $data->TOKEN,
]);
/**
* I used this library but it doesn't work for me
* https://github.com/akrabat/rka-slim-session-middleware
*/
$this->sesion->data = $data->TOKEN;
$this->sesion->id_empresa = $data->id_empresa;
$this->sesion->id_usuario = $data->id_admin;
/**
* I create session variables with the local system, but they don't work either
*/
$_SESSION['data'] = $data->TOKEN;
$_SESSION['id_empresa'] = $data->id_empresa;
$_SESSION['id_usuario'] = $data->id_admin;
$this->response->data = array("TOKEN" => $token);
return $this->response->SetResponse(true);
}else{
return $this->response->SetResponse("false", "Credenciales no válidas");
}
}
The problem arises when I want to access some resource of my API, I have a Middelwere configured, I don't know if this is causing the inconvenience. For example, in the following resources, when I access the model, my "user_id" and "business_id" session variables do not exist.
Step 2. I try to access a route (Resource). The resource requests access to the Middelwere
$app->group('/agenda/', function () {
$this->get('get', function ($req, $res, $args) {
return $res->withHeader('Content-type', 'application/json')
->write(
json_encode($this->model->agenda->getAll())
);
});
})->add(new AuthMiddlewareEmpresa($app));
Step 2.1 The Middleware validates the permissions to access the resource.
class AuthMiddlewareEmpresa{
private $app = null;
private $r = null;
public function __construct($app){
$this->app = $app;
$this->r = new Response;
}
public function __invoke($request, $response, $next){
$c = $this->app->getContainer();
$app_tokken_name = $c->settings['app_token_name'];
$token = $request->getHeader($app_tokken_name);
if($c->model->detallesempresa->obtener()->data!=false && $c->model->detallesempresa->obtener()->data->api==1){
if(isset($token[0])) $token = $token[0];
try{
Auth::Check($token);
}catch(Exception $e){
$r = new Response;
$r->data = array("TOKEN" => null);
return $response->withHeader('Content-type', 'application/json')->withStatus(401)->write(json_encode($r->SetResponse(false, "Token no válido.")));
}
}else{
$r = new Response;
return $response->withHeader('Content-type', 'application/json')->withStatus(401)->write(json_encode($r->SetResponse(false, "Sin acceso a Jobtify API.")));
}
return $next($request, $response);
}
}
but when the Middleware requires access to the model to validate the permissions from the database. I simply cannot access the session variables. T_T
2.3. This is the model. Return null because when doing the queries it does not detect the session variables user_id and company_id
class DetallesEmpresaModel{
private $db;
private $TOKEN = "";
private $id_empresa = "";
private $id_usuario = "";
private $response;
private $sesion;
public function __construct($db){
$this->db = $db;
$this->response = new Response();
$this->sesion = new \RKA\Session();
if(isset($this->sesion->id_empresa)){
$this->TOKEN = $_SESSION['data'];
}
if(isset($this->sesion->id_empresa)){
$this->id_empresa = $this->sesion->id_empresa;
}
if(isset($this->sesion->id_usuario)){
$this->id_usuario = $this->sesion->id_usuario;
}
}
/*
Este modelo es accesible internamente
*/
public function obtener(){
try{
$sql="SELECT e.id_paquete AS paquete, e.activa, e.api, priv.* FROM empresas_privilegios AS priv
INNER JOIN empresas AS e ON priv.id_empresa=e.id
WHERE id_empresa=:id_empresa";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(":id_empresa", $this->id_empresa, PDO::PARAM_STR);
$respuesta = $stmt ->execute();
$respuesta = $stmt ->fetch();
$data = $respuesta;
}catch(Exception $e){
die($e);
}
$this->response->SetResponse(true);
$this->response->data = $data;
return $this->response;
}
}

Related

How to adapt my code to meet PSR standards: PSR-7, PSR-15

I am brand new to PSR standards, and I am not sure if I adapted my code to PSR-7, PSR-15 correctly.
My code is handling a POST request to delete a group of products by receiving an array of ids.
Is that a correct adaptation? Thanks.
<?php
require_once 'DataBase.php';
require_once 'config.php';
use Psr\Http\Server\RequestHandlerInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Message\ResponseInterface;
class DeleteRequest implements RequestHandlerInterface
{
private $DB;
public function __construct(DataBase $DB)
{
$this->DB = $DB;
}
//Delete each product from the database using the ID
public function handle(ServerRequestInterface $request): ResponseInterface
{
// Make sure it is a POST request
if ($request->getMethod() !== 'POST') {
throw new Exception('Incorrect REQUEST_METHOD. '.
'Only POST requests are allowed.');
}
// Extract the 'ids' array from the request data
MyLogV($request->getBody()->getContents());
$data = json_decode($request->getBody()->getContents(), true);
// Make sure the 'ids' array is present in the data
if (!isset($data['ids'])) {
throw new Exception('Missing required parameter: ids');
}
$ids = $data['ids'];
foreach ($ids as $id) {
myLog("DeleteRequest->handle","id",$id);
$result = $this->DB->deleteProduct($id);
if ($result['status'] != 'success') break;
}
// Generate the response: 200 => OK, 400 => Bad request
$status = $result['status'] == 'success' ? 200 : 400;
$response = new JsonResponse($result, $status);
myLogV($result['status']);
return $response;
}
}
try {
$serverRequest = ServerRequestFactory::fromGlobals();
$DB = new DataBase();
$deleteRequest = new DeleteRequest($DB);
$response = $deleteRequest->handle($serverRequest);
$response->send();
} catch (Exception $e) {
myLog("delete.php","Exception",$e->getMessage());
$result = ['status' => 'error','message'=> $e->getMessage()];
$response = new JsonResponse($result, 400);
$response->send();
}
exit();
?>
I tried to understand the PSR standards.

Joomla user login, register, start session

I'm preparing joomla plugin/api to connect website with mobileapp. I cannot use cookies, I need to do this only by request.
So to login I can go to http://example.net/?user=aaa&pass=bbb and it creates session and returns token.
To go to user profile I can go to: http://example.net/profile?token=8asd7g... and if token matches session id in database then it sets session cookie (on php side) in Joomla framework
Which event should I use to:
check token and maintain session
check login, user and login/create session
check login, user and register user
The second question is how:
is it enought to set $session->set('userid',$user->id); ?
I create fake $response and $app->triggerEvent('onUserLogin', array((array) $response, array('action' => 'core.login.admin'))); is it enough?
If somebody need here is almost fnished solution. It allows users to login via user and pass taken form url or request header, it allows access to restricted parts of website based on token i url not cookie, it allows to pass params from header to JInput->get.
<?php
/**
* #package API
* #subpackage System.sittetokenlogin
*
*/
defined('_JEXEC') or die('Unauthorized Access');
jimport('joomla.filesystem.file');
class PlgSystemSittetokenlogin extends JPlugin
{
public function __construct(&$subject, $config)
{
parent::__construct($subject, $config);
}
public function onUserAuthenticate()
{
//die('onUserAuthenticate');
}
public function onUserLogin()
{//wykonuje się
//die('onUserLogin');
}
public function onUserLogout()
{//wykonuje się
//die('onUserLogout');
}
public function onAfterInitialise()
{
//wstępne ustawienie obiektów
$app = JFactory::getApplication();
if ($app->isClient('administrator')) return;
$input = JFactory::getApplication()->input;
$headers = getallheaders ();
$db = JFactory::getDbo();
//pobranie danych z rządania
$loginToken = $headers['logintoken']; if(!$loginToken) $loginToken = $input->get->get('logintoken', '', 'STRING');
$suser = $headers['suser']; if(!$suser) $suser = $input->get->get('suser', '', 'STRING');
$spass = $headers['spass']; if(!$spass) $spass = $input->get->get('spass', '', 'STRING');
if ($loginToken) // logowanie na bazie tokenu
{
JPluginHelper::importPlugin('user');
$sesja = $db->setQuery('SELECT * FROM `#__session` WHERE `session_id`='.$db->quote($loginToken).' LIMIT 1')->loadObject();
$user = $db->setQuery('SELECT * FROM `#__users` WHERE `id`='.$db->quote($sesja->userid).' LIMIT 1')->loadObject();
$response = new JAuthenticationResponse();
$response->type = 'Joomla';
$response->email = $user->email;
$response->fullname = $user->name;
$response->username = $user->username;
$response->password = '';
$response->status = JAuthentication::STATUS_SUCCESS;
$response->error_message = null;
//print_r($response);
$app->triggerEvent('onUserLogin', array((array) $response, array('action' => 'core.login.site')));
//$testuser = JFactory::getUser(); die(print_r($testuser,true));
}
elseif ($suser && $spass) //logowanie na bazie loginu i hasła
{
$error = $app->login([
'username' => $suser,
'password' => $spass,
]);
$user = JFactory::getUser();
if ($user->id>0) die(JFactory::getSession()->getId());
else die('login_error');
}
//przekazywanie parametrów
$option = $headers['option']; $input->set('option',$option);
$view = $headers['view']; $input->set('view',$view);
$id = $headers['id']; $input->set('id',$id);
$catid = $headers['catid']; $input->set('catid',$catid);
$Itemid = $headers['Itemid']; $input->set('Itemid',$Itemid);
$tmpl = $headers['tmpl']; $input->set('tmpl',$tmpl);
//$input->set('option','com_guru');
//$input->set('view','gurupcategs');
}
}

How save access_token to db using yii2-dektrium facebook login?

I'm using yii2-dektrium to allow users login with their facebook's accounts.
After the login is done, I need to make API request from my server to get data of the user's accounts. One example of request is:
$client = Yii::$app->authClientCollection->getClient('facebook');
$response = $client->createApiRequest()
->setMethod('GET')
->setUrl('v2.12/me/accounts')
->send();
The access_token is saved on session so I need to persist it to the database.
I already added a column access_token to the social_account default table of yii2-dektrium but I don't know how to get and save it, and further more, how to apply it to the requests.
After reading for a while. I think the way to save it is overriding the method connect in dektrium\user\controllers\SecurityController.
public function connect(ClientInterface $client)
{
/** #var Account $account */
$account = \Yii::createObject(Account::className());
$event = $this->getAuthEvent($account, $client);
$this->trigger(self::EVENT_BEFORE_CONNECT, $event);
$account->connectWithUser($client);
$this->trigger(self::EVENT_AFTER_CONNECT, $event);
$this->action->successUrl = Url::to(['/user/settings/networks']);
}
And for applying to the request, override applyAccessTokenToRequest on yii\authclient\clients\Facebook
public function applyAccessTokenToRequest($request, $accessToken)
{
parent::applyAccessTokenToRequest($request, $accessToken);
$data = $request->getData();
if (($machineId = $accessToken->getParam('machine_id')) !== null) {
$data['machine_id'] = $machineId;
}
$data['appsecret_proof'] = hash_hmac('sha256', $accessToken->getToken(), $this->clientSecret);
$request->setData($data);
}
I can't get it done. And I'm not sure if it is the right way to do it. What I'm missing?
For save the access_token the first time you have to overwrite the connect action from \dektrium\user\controllers\SecurityController.
class SecurityController extends \dektrium\user\controllers\SecurityController
{
public function connect(ClientInterface $client)
{
// default implementation of connect
$account = \Yii::createObject(Account::className());
$event = $this->getAuthEvent($account, $client);
$this->trigger(self::EVENT_BEFORE_CONNECT, $event);
$account->connectWithUser($client);
$this->trigger(self::EVENT_AFTER_CONNECT, $event);
// get acess_token from $client
$access_token['tokenParamKey'] = $client->getAccessToken()->tokenParamKey;
$access_token['tokenSecretParamKey'] = $client->getAccessToken()->tokenSecretParamKey;
$access_token['createTimestamp'] = $client->getAccessToken()->createTimestamp;
$access_token['_expireDurationParamKey'] = $client->getAccessToken()->getExpireDurationParamKey();
$access_token['_params'] = $client->getAccessToken()->getParams();
// save acess_token to social_account table
$model = SocialAccount::find()->where(['provider' => $client->getName()])->andWhere(['user_id' => Yii::$app->user->id])->one();
$model->access_token = \yii\helpers\Json::encode($access_token);
$model->save(false);
$this->action->successUrl = Url::to(['/user/settings/networks']);
}
}
To get the access_token store in the database for further API Requests create a class that extends yii\authclient\SessionStateStorage and overwrite get method.
namespace app\models\authclient;
class DbStateStorage extends SessionStateStorage
{
public function get($key)
{
// $key is a complex string that ends with 'token' if the value to get is the actual access_token
$part = explode('_', $key);
if (count($part) == 3 && $part[2] == 'token') {
$account = SocialAccount::find()
->where(['provider' => $part[1]])
->andWhere(['user_id' => Yii::$app->user->id])
->one();
if ($account != null) {
$access_token = json_decode($account->access_token);
$token = new \yii\authclient\OAuthToken();
$token->createTimestamp = $access_token->createTimestamp;
$token->tokenParamKey = $access_token->tokenParamKey;
$token->tokenSecretParamKey = $access_token->tokenSecretParamKey;
$token->setParams((array)$access_token->_params);
$token->setExpireDurationParamKey($access_token->_expireDurationParamKey);
return $token;
}
}
if ($this->session !== null) {
return $this->session->get($key);
}
return null;
}
}
Finally set the DbStateStorage to your authclient
class Facebook extends \dektrium\user\clients\Facebook
{
public function __construct()
{
$this->setStateStorage('app\models\authclient\DbStateStorage');
}
}

angular2 with Slim framework jwt authentication

I am trying to create a survice to authenticate user name and password in angular2.
Here is the code for authentication.service.ts
import { Injectable } from '#angular/core';
import { Http, Headers, Response } from '#angular/http';
import { Observable } from 'rxjs';
import {Md5} from 'ts-md5/dist/md5';
export interface User {
userName: string;
password: string; }
#Injectable()
export class AuthenticationService {
public token: string;
constructor(private http: Http) {
// set token if saved in local storage
var currentUser = JSON.parse(localStorage.getItem('currentUser'));
this.token = currentUser && currentUser.token;
}
login(user:User): Observable {
return this.http.post('http://localhost/hj1/api/authenticate',
JSON.stringify({ 'user': user.userName, 'password': Md5.hashStr(user.password) }))
.map((response: Response) => {
// login successful if there's a jwt token in the response
console.log(response);
let token = response.json() && response.json().token;
if (token) {
// set token property
this.token = token;
// store username and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('currentUser', JSON.stringify({ user: user, token: token }));
// return true to indicate successful login
return true;
} else {
// return false to indicate failed login
return false;
}
}
);
}
logout() {
localStorage.removeItem("currentUser");
this.token = null;
}
}
and this is my index.php using slim framework
getContainer();
$container["jwt"] = function ($container) {
return new StdClass;
};
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => "/",
"passthrough" => "/authenticate",
"secret" => getenv("HJ_ENV"),
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
},
"callback" => function ($request, $response, $arguments) use ($container) {
$body = $response->getBody();
$body->write($arguments["decoded"]);
$container["jwt"] = $arguments["decoded"];
}
]));
$app->post('/authenticate', 'authenticate');
$app->run();
function authenticate(Request $request, Response $response)
{
$params = json_decode($request->getBody());
$sql = "select * from users where userName = :userName";
$result = json_decode( runQuery($sql, [ ':userName', $params->user ]) );
$body = $response->getBody();
if ( $result && $result[0]->password == $params->password )
{
$decoded = $request->getAttribute("jwt");
$body->write( json_encode([ 'token' => $decoded ]) );
}
else
{
$body->write( json_encode(['token' => null]) );
}
}
function runQuery($sql, ...$params)
{
try
{
$db = getConnection();
$stmt = $db->prepare($sql);
foreach ( $params as $param )
{
$stmt->bindParam( $param[0], $param[1] );
}
$stmt->execute();
$rows = [];
while($row=$stmt->fetch(PDO::FETCH_OBJ))
{
/*its getting data in line.And its an object*/
array_push($rows, $row );
}
$db = null;
return json_encode($rows);
}
catch(PDOException $e)
{
$db = null;
return $e->getMessage() ;
}
}
?>
my question is
I am not able to get token from container['jwt'].
If i give incorrect user name and password then token remain null.
But if i give correct user name and password. the $result variable give me data from my database. i can verify password. but $request->getAttribute("jwt") this method gives me null.
also i have checked $decoded = $container["jwt"]
but this also gives me null.
SO i could not know how to get the token created by jwt.
Thank you.
add(new \Slim\Middleware\JwtAuthentication([
"path" => "/",
"passthrough" => "/authenticate",
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"] ;
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES |
JSON_PRETTY_PRINT));
}
]));
$app->post('/authenticate', function (Request $request, Response $response )
{
$params = json_decode($request->getBody());
/* $params will contain user and password posted by angular for
verification in data base */
/* here you retrieve user name and password from database */
if ( /* check here user name and password */ )
{
$now = new DateTime();
$future = new DateTime("now +2 hours");
$payload = [
"iat" => $now->getTimeStamp(),
"exp" => $future->getTimeStamp()
];
$secret = getenv("HJ_ENV"); /* put your secret key here */
$token = JWT::encode($payload, $secret, "HS256");
$data["status"] = "ok";
$data["token"] = $token;
return $response->withStatus(201)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES |
JSON_PRETTY_PRINT));
}
else
{
$data["status"] = "error";
$data["message"] = "Invalid Token" ;
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES |
JSON_PRETTY_PRINT));
}
});

Slim post method redirect does not Work with slim Middleware

Hey guys i got some Problems with the Slim Middleware.
I created a Middleware that checks if the user is logged with Facebook and has a specific Email address. So now when i call the url with the PHPStorm RESTful Test tool i should not be able to post data to the server...
But the Redirect does not work so i will be able to send data to the server.
/**
* Admin Middleware
*
* Executed before /admin/ route
*/
$adminPageMiddleware = function ($request, $response, $next) {
FBLoginCtrl::getInstance();
$user = isset($_SESSION['user']) ? $_SESSION['user'] : new User();
if (!($user->getEmail() == ADMIN_USER_EMAIL)) {
$response = $response->withRedirect($this->router->pathFor('login'), 403);
}
$response = $next($request, $response);
return $response;
};
/**
* Milestone POST Method
*
* Create new Milestone
*/
$app->post('/admin/milestone', function (Request $request, Response $response) use ($app) {
$milestones = $request->getParsedBody();
$milestones = isset($milestones[0]) ? $milestones : array($milestones);
foreach ($milestones as $milestone) {
$ms = new Milestone();
$msRepo = new MilestoneRepository($ms);
$msRepo->setJsonData($milestone);
if (!$msRepo->createMilestone()) {
return $response->getBody()->write("Not Okay");
};
}
return $response->getBody()->write("Okay");
})->add($adminPageMiddleware);
So can anyone give me a hint what the problem could be?
I tried to add the same middleware to the get Route ... there it works :/ Strange stuff.
The problem is in your middleware logic.
if (!($user->getEmail() == ADMIN_USER_EMAIL)) {
return $response->withRedirect($this->router->pathFor('login'), 403); //We do not want to continue execution
}
$response = $next($request, $response);
return $response;
So now i ended up with this code:
class AdminRouteMiddleware
{
public function __invoke($request, $response, $next)
{
FBLoginCtrl::getInstance();
$user = isset($_SESSION['user']) ? $_SESSION['user'] : new User();
if (!($user->getEmail() == ADMIN_USER_EMAIL)) {
if ($_SERVER['REQUEST_METHOD'] == "GET") {
$response = $response->withRedirect('/login', 403);//want to use the route name instead of the url
} else {
$response->getBody()->write('{"error":Access Denied"}');
}
} else {
$response = $next($request, $response);
}
return $response;
}
}
/**
* Milestone POST Method
*
* Create new Milestone
*/
$app->post('/admin/milestone', function (Request $request, Response $response) use ($app) {
$milestones = $request->getParsedBody();
$milestones = isset($milestones[0]) ? $milestones : array($milestones);
foreach ($milestones as $milestone) {
$ms = new Milestone();
$msRepo = new MilestoneRepository($ms);
$msRepo->setJsonData($milestone);
if (!$msRepo->createMilestone()) {
return $response->getBody()->write("Not Okay");
};
}
return $response->getBody()->write("Okay");
})->add(new AdminRouteMiddleware());

Categories