Unexpected Value exception - php

I am having below error in postman while i was teting my api. It shows slim application error in postman. Error Type: UnexpectedValueException
Message: Wrong number of segments
File: C:\Users\tahmeed\Documents\app-timber-api2\vendor\firebase\php-jwt\src\JWT.php
Line: 78
Do i need to modify the token or the JWT.php ?
decode.php in JWT.php
public static function decode($jwt, $key, array $allowed_algs = array())
{
$timestamp = is_null(static::$timestamp) ? time() : static::$timestamp;
if (empty($key)) {
throw new InvalidArgumentException('Key may not be empty');
}
$tks = explode('.', $jwt);
if (count($tks) != 3) {
throw new UnexpectedValueException('Wrong number of segments');
}
list($headb64, $bodyb64, $cryptob64) = $tks;
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
throw new UnexpectedValueException('Invalid header encoding');
}
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
throw new UnexpectedValueException('Invalid claims encoding');
}
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
throw new UnexpectedValueException('Invalid signature encoding');
}
if (empty($header->alg)) {
throw new UnexpectedValueException('Empty algorithm');
}
if (empty(static::$supported_algs[$header->alg])) {
throw new UnexpectedValueException('Algorithm not supported');
}
if (!in_array($header->alg, $allowed_algs)) {
throw new UnexpectedValueException('Algorithm not allowed');
}
if (is_array($key) || $key instanceof \ArrayAccess) {
if (isset($header->kid)) {
if (!isset($key[$header->kid])) {
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
}
$key = $key[$header->kid];
} else {
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
}
}
// Check the signature
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
throw new SignatureInvalidException('Signature verification failed');
}
// Check if the nbf if it is defined. This is the time that the
// token can actually be used. If it's not yet that time, abort.
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->nbf)
);
}
// Check that this token has been created before 'now'. This prevents
// using tokens that have been created for later use (and haven't
// correctly used the nbf claim).
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
throw new BeforeValidException(
'Cannot handle token prior to ' . date(DateTime::ISO8601, $payload->iat)
);
}
// Check if this token has expired.
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
throw new ExpiredException('Expired token');
}
return $payload;
}
AuthController.php
<?php
namespace App\Controllers\AppodMobile;
use \Psr\Http\Message\ServerRequestInterface as Request;
use \Psr\Http\Message\ResponseInterface as Response;
use \Interop\Container\ContainerInterface as ContainerInterface;
use \Illuminate\Database\Query\Expression as Raw;
use App\Models\AppodMobile\Users as Users;
use Firebase\JWT\JWT;
use Tuupola\Base62;
class AuthController
{
use \App\CommonFunctions;
protected $container;
public function __construct(ContainerInterface $container) {
$this->container = $container;
}
function auth($request,$response)
{
$input = $request->getParsedBody();
$user = Users::select('id','pword')->where('email','=',$input['email'])->first();
// verify email address.
if(!$user) {
$response->withStatus(404);
return $response->withJson(['error' => true, 'message' => 'User does not exist.'],404);
}
// verify password.
$salt = getenv('TMS_SALT');
if (!(sha1($salt.$input['password']) == $user->pword)) {
$response->withStatus(401);
return $response->withJson(['error' => true, 'message' => 'Password is incorrect.'],401);
}
$now = new \DateTime();
$future = new \DateTime("+120 minutes");
$server = $request->getServerParams();
$jti = (new Base62)->encode(random_bytes(16));
$payload = [
"iat" => $now->getTimeStamp(),
// "exp" => $future->getTimeStamp(),
"jti" => $jti,
"sub" => $server["PHP_AUTH_USER"]
];
$token = JWT::encode($payload, getenv('JWT_SECRET'), "HS256");
$data = array(
'token' => $token,
'user_id'=>$user->id,
// appod'expires' => $future->getTimestamp()
);
$response->withStatus(200);
return $response->withJson($data);
}
}

You should use third paramater in Decode Method to solve the Uncaught UnexpectedValueException: Algorithm not allowed
Here is the below code
<?php;
require('jwt/vendor/autoload.php');
use \Firebase\JWT\JWT;
function generate_token($uid){
$key = "thiismykey";
$jwt = JWT::encode($uid, $key);
echo "JWT Toke = ".$jwt."<br>";
$decoded = JWT::decode($jwt, $key, array('HS256'));
echo "After Encode = ".$decoded;
}
//call the funtion
generate_token("santosh");
?>
Output -
JWT Toke = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.InNhbnRvc2gi.ZUyzpLH0FLB9VdRPS2CaQAqM_wKHjXP80moIzL-8u2o
After Encode = santosh

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.

Always get exception when login with JWT Management Session

I try to learn how to manage session with JWT. But now I'm stuck because i always get exception after login, and it always redirect me to login page again and again.
There is no error detected, and i can't find what's wrong in my code. So i hope you all can help me although it is just look like a simple questions
I try to browse to check where is the problem. And i'm sure the problem is in the file session.php. But i don't know whats wrong
<?php
// use Firebase\JWT\JWT;
class Session
{
public $username, $role;
public function __construct(string $username, string $role)
{
$this->username = $username;
$this->role = $role;
}
}
class SessionManager
{
// public $SECRET_KEY = 'ajfhakjdfhah/A203FHkafhiuefhhncueuvuwevwevwev';
public static function login(string $username, string $password): bool
{
if ($username == "eko" && $password == "eko") {
$SECRET_KEY = 'AKDJHFEVN123akdhfvbuevmkc';
$payload = [
"username" => $username,
"role" => "customer"
];
$jwt = \Firebase\JWT\JWT::encode($payload, $SECRET_KEY, 'HS256');
setcookie('USER-SESSION', $jwt);
return true;
} else {
return false;
}
}
public static function getCurrentSession(): Session
{
if ($_COOKIE['USER-SESSION']) {
$jwt = $_COOKIE['USER-SESSION'];
$SECRET_KEY = 'AKDJHFEVN123akdhfvbuevmkc';
$payload = \Firebase\JWT\JWT::decode($jwt, $SECRET_KEY, ['HS256']);
try {
$payload = \Firebase\JWT\JWT::decode($jwt, $SECRET_KEY, ['HS256']);
return new Session($payload->username, $payload->role);
} catch (Exception $exception) {
throw new Exception("User is not login");
}
} else {
throw new Exception("User is not login");
}
}
}

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));
}
});

Async HTML parser with Goutte

I am trying to write HTML parser with the help of Goutte. It works very well. However Goutte uses blocking requests. This works well if you are dealing with a single service. If I want to query lots of services which are independent from each other, this causes a problem. Goutte uses BrowserKit and Guzzle. I have tried to change doRequest function but it failed with
Argument 1 passed to
Symfony\Component\BrowserKit\CookieJar::updateFromResponse() must be
an instance of Symfony\Component\BrowserKit\Response
protected function doRequest($request)
{
$headers = array();
foreach ($request->getServer() as $key => $val) {
$key = strtolower(str_replace('_', '-', $key));
$contentHeaders = array('content-length' => true, 'content-md5' => true, 'content-type' => true);
if (0 === strpos($key, 'http-')) {
$headers[substr($key, 5)] = $val;
}
// CONTENT_* are not prefixed with HTTP_
elseif (isset($contentHeaders[$key])) {
$headers[$key] = $val;
}
}
$cookies = CookieJar::fromArray(
$this->getCookieJar()->allRawValues($request->getUri()),
parse_url($request->getUri(), PHP_URL_HOST)
);
$requestOptions = array(
'cookies' => $cookies,
'allow_redirects' => false,
'auth' => $this->auth,
);
if (!in_array($request->getMethod(), array('GET', 'HEAD'))) {
if (null !== $content = $request->getContent()) {
$requestOptions['body'] = $content;
} else {
if ($files = $request->getFiles()) {
$requestOptions['multipart'] = [];
$this->addPostFields($request->getParameters(), $requestOptions['multipart']);
$this->addPostFiles($files, $requestOptions['multipart']);
} else {
$requestOptions['form_params'] = $request->getParameters();
}
}
}
if (!empty($headers)) {
$requestOptions['headers'] = $headers;
}
$method = $request->getMethod();
$uri = $request->getUri();
foreach ($this->headers as $name => $value) {
$requestOptions['headers'][$name] = $value;
}
// Let BrowserKit handle redirects
$promise = $this->getClient()->requestAsync($method,$uri,$requestOptions);
$promise->then(
function (ResponseInterface $response) {
return $this->createResponse($response);
},
function (RequestException $e) {
$response = $e->getResponse();
if (null === $response) {
throw $e;
}
}
);
$promise->wait();
}
How can I change Goutte\Client.php so that it does requests asynchronously? Is that is not possible, how can I run my scrappers which targets different endpoints simultaneously? Thanks
Goutte is essentially a bridge between Guzzle and Symphony's Browserkit and DomCrawler.
The biggest drawback with using Goutte is that all requests are made sychronouslly
To complete things asychronously you will have to forego using Goutte and directly use Guzzle and DomCrawler.
For example:
$requests = [
new GuzzleHttp\Psr7\Request('GET', $uri[0]),
new GuzzleHttp\Psr7\Request('GET', $uri[1]),
new GuzzleHttp\Psr7\Request('GET', $uri[2]),
new GuzzleHttp\Psr7\Request('GET', $uri[3]),
new GuzzleHttp\Psr7\Request('GET', $uri[4]),
new GuzzleHttp\Psr7\Request('GET', $uri[5]),
new GuzzleHttp\Psr7\Request('GET', $uri[6]),
];
$client = new GuzzleHttp\Client();
$pool = new GuzzleHttp\Pool($client, $requests, [
'concurreny' => 5, //how many concurrent requests we want active at any given time
'fulfilled' => function ($response, $index) {
$crawler = new Symfony\Component\DomCrawler\Crawler(null, $uri[$index]);
$crawler->addContent(
$response->getBody()->__toString(),
$response->getHeader['Content-Type'][0]
);
},
'rejected' => function ($response, $index) {
// do something if the request failed.
},
]);
$promise = $pool->promise();
$promise->wait();

Categories