SLIM Framework Route Authentication v2 vs v3 - php

I have an API built with Slim v2 and I secure certain routes passing a middleware function "authenticate":
/**
* List marca novos
* method GET
* url /novos/marca/:idmarca
*/
$app->get('/novos/marca/:idmarca', 'authenticate', function($idmarca) {
$response = array();
$db = new DbHandler('dbnovos');
// fetching marca
$marca = $db->getMarcaNovos($idmarca);
$response["error"] = false;
$response["marca"] = array();
array_walk_recursive($marca, function(&$val) {
$val = utf8_encode((string)$val);
});
array_push($response["marca"], $marca);
echoRespnse(200, $response, "marcaoutput");
})->via('GET', 'POST');
The authenticate function checks if a headers Authorization value was sent (user_api_key) and checks it against the database.
I'm trying to get the same functionality in a Slim v3 API with the folowwing route:
/**
* List marca novos
* method GET
* url /novos/marca/:idmarca
*/
$app->get('/novos/marca/{idmarca}', function ($request, $response, $args) {
$output = array();
$db = new DbHandler('mysql-localhost');
$marca = $db->getMarcaNovos($args['idmarca']);
if ($marca != NULL) {
$i = 0;
foreach($marca as $m) {
$output[$i]["id"] = $m['id'];
$output[$i]["nome"] = utf8_encode($m['nome']);
$i++;
}
} else {
// unknown error occurred
$output['error'] = true;
$output['message'] = "An error occurred. Please try again";
}
// Render marca view
echoRespnse(200, $response, $output, "marca");
})->add($auth);
This is my middleware
/**
* Adding Middle Layer to authenticate every request
* Checking if the request has valid api key in the 'Authorization' header
*/
$auth = function ($request, $response, $next) {
$headers = $request->getHeaders();
$outcome = array();
// Verifying Authorization Header
if (isset($headers['Authorization'])) {
$db = new DbHandler('mysql-localhost');
// get the api key
$api_key = $headers['Authorization'];
// validating api key
if (!$db->isValidApiKey($api_key)) {
// api key is not present in users table
$outcome["error"] = true;
$outcome["message"] = "Access Denied. Invalid Api key";
echoRespnse(401, $outcome, $output);
} else {
global $user_id;
// get user primary key id
$user_id = $db->getUserId($api_key);
$response = $next($request, $response);
return $response;
}
} else {
// api key is missing in header
$outcome["error"] = true;
$outcome["message"] = "Api key is missing";
//echoRespnse(400, $response, $outcome);
return $response->withStatus(401)->write("Not allowed here - ".$outcome["message"]);
}
};
But I always get the error: "Not allowed here - Api key is missing"
Basically, the test if $headers['Authorization'] is set is failing. What is the $headers array structure or how do I get the Authorization value passed through the header?

If you are sending something else than valid HTTP Basic Authorization header, PHP will not have access to it. You can work around this by adding the following rewrite rule to your .htaccess file.
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

Related

Cant match local token to passed GoCardless token

I'm using laravel 5.3 with the GoCardless API and I'm trying to build out the endpoints so my application can handle events accordingly.
I'm reading the GoCardless documentation and it says do the following:
$token = getenv("GC_WEBHOOK_SECRET");
$raw_payload = file_get_contents('php://input');
$headers = getallheaders();
$provided_signature = $headers["Webhook-Signature"];
$calculated_signature = hash_hmac("sha256", $raw_payload, $token);
if ($provided_signature == $calculated_signature) {
} else {
};
I've converted the above into Laravel friendly controller speak.
public function hook(Request $request)
{
$token = env("GC_WEBHOOK_SECRET");
$raw_payload = $request;
$provided_signature = $request->header('Webhook-Signature');
$calculated_signature = hash_hmac("sha256", $raw_payload, $token);
if ($provided_signature == $calculated_signature) {
Log::info('It was a match!');
} else {
Log::info('Something went wrong!');
};
}
but I can't get the $provided_signature to match the $calculated_signature, I've got a feeling it's something to do with the way I'm hashing the token from my env file.
Solved it guys, all I needed to do was replace
$raw_payload = $request;
with
$raw_payload = $request->getContent();

Notice: Undefined offset: 0 in C:\wamp64\www\lynda2\src\Chatter\Middleware\Authentication.php on line 12

Hi i'm created a web service with Slim from a course of lynda "Building APIs in PHP Using the Slim Micro Framework" but when i want login, this error Occurs
Notice: Undefined offset: 0 in C:\wamp64\www\lynda2\src\Chatter\Middleware\Authentication.php on line 12
Authentication
namespace Chatter\Middleware;
use Chatter\Models\User;
class Authentication
{
public function __invoke($request, $response, $next)
{
$auth = $request->getHeader('Authorization');
$_apikey = $auth[0];
$apikey = substr($_apikey, strpos($_apikey, ' ') + 1);
$user = new User();
if (!$user->authenticate($apikey)) {
$response->withStatus(401);
return $response;
}
$response = $next($request, $response);
return $response;
}
}
User.php
<pre><code>
namespace Chatter\Models;
class User extends \Illuminate\Database\Eloquent\Model
{
public function authenticate($apikey)
{
$user = User::where('apikey', '=', $apikey)->take(1)->get();
$this->details = $user[0];
return ($user[0]->exists) ? true : false;
}
}
</code></pre>
index.php
<pre><code>
require 'vendor/autoload.php';
include 'bootstrap.php';
use Chatter\Models\Message;
use Chatter\Middleware\Logging as ChatterLogging;
use Chatter\Middleware\Authentication as ChatterAuth;
$app = new \Slim\App();
$app->add(new ChatterAuth());
$app->add(new ChatterLogging());
$app->get('/messages', function ($request, $response, $args) {
$_message = new Message();
$messages = $_message->all();
$payload = [];
foreach($messages as $_msg) {
$payload[$_msg->id] = ['body' => $_msg->body, 'user_id' => $_msg->user_id, 'created_at' => $_msg->created_at];
}
return $response->withStatus(200)->withJson($payload);
});
$app->get('/', function ($request, $response, $args) {
return "This is a catch all route for the root that doesn't do anything useful.";
});
// Run app
$app->run();
</code></pre>
The error is stating that when you "login" there is no Authorization header present.
$request->getHeader('Authorization') returns an empty array, so when you attempting to access the first element of the array, you get your error:
$_apikey = $auth[0]; // Will trigger error, since there are no elements in the array
Thus to aviod this error, get $apikey like this:
public function __invoke($request, $response, $next)
{
$auth = $request->getHeader('Authorization');
$_apikey = array_shift($auth);
if ($_apikey) {
$apikey = substr($_apikey, strpos($_apikey, ' ') + 1);
$user = new User();
if (!$user->authenticate($apikey)) {
return $response->withStatus(401);
} else {
return $next($request, $response);
}
} else {
// Authorization header is missing, therefore unauthorized access
return $response->withStatus(401);
}
}
This is an older thread, but in case anyone else is following this tutorial ... the code the OP posted was supposed to do exactly what it does - to fail if there is no authorization header present.
Looks like the OP missed one step: adding the bearer token to the request. In Postman, go to Authorization > Type > Bearer Token and paste a valid token in the input field. I believe that it was clearly stated in the tutorial. Afterward, everything works as expected.

Laravel - JWT Auth The token could not be parsed from the request

I have added following code in my middleware for user authentication with JWT Auth, which works fine for all the routes handled by the middleware.
public function handle($request, Closure $next)
{
if ($request->has('token')) {
try {
$this->auth = JWTAuth::parseToken()->authenticate();
return $next($request);
} catch (JWTException $e) {
return redirect()->guest('user/login');
}
}
}
But for one route with Post Method where the token is getting passed properly but still I am getting :
JWTException - The token could not be parsed from the request
on the same route when I tried :
public function handle($request, Closure $next)
{
if ($request->has('token')) {
try {
dd($request->input('token'));
$this->auth = JWTAuth::parseToken()->authenticate();
return $next($request);
} catch (JWTException $e) {
return redirect()->guest('user/login');
}
}
}
Output :
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9iaXNzIjoiaHR0cDpcL1wvbG9jYWxob3N0OjgwMDFcL2F1dGhcL2xvZ2luIiwiaWF0IjoxNDcyNTI4NDU0LCJleHAiOjE0NzI1MzIwNTQsIm5iZiI6MTQ3MjUyODQ1NCwianRpIjoiM2E0M2ExYTZlNmM5NjUxZDgxYjZhNDcxMzkxODJlYjAifQ.CH8ES2ADTCrVWeIO8uU31bGDnH7h-ZVTWxrdXraLw8s"
I am able to see the Valid Token which I am using to access another routes and which is working flawlessly for all other routes.
Thanks in advance!!!
From your description, I checked source file of JWT Auth.
In class Tymon\JWTAuth\JWTAuth line 191 - 219 , there are two functions:
/**
* Parse the token from the request.
*
* #param string $query
*
* #return JWTAuth
*/
public function parseToken($method = 'bearer', $header = 'authorization', $query = 'token')
{
if (! $token = $this->parseAuthHeader($header, $method)) {
if (! $token = $this->request->query($query, false)) {
throw new JWTException('The token could not be parsed from the request', 400);
}
}
return $this->setToken($token);
}
/**
* Parse token from the authorization header.
*
* #param string $header
* #param string $method
*
* #return false|string
*/
protected function parseAuthHeader($header = 'authorization', $method = 'bearer')
{
$header = $this->request->headers->get($header);
if (! starts_with(strtolower($header), $method)) {
return false;
}
return trim(str_ireplace($method, '', $header));
}
Check the logic of them, I believe your request header is not properly provided.
if (! $token = $this->parseAuthHeader($header, $method)) { // all your get method not passed this step
if (! $token = $this->request->query($query, false)) { // all your post method stucked here
throw new JWTException('The token could not be parsed from the request', 400);
}
}
A properly formatted header looks like this :
http POST http://${host}/api/v1/product/favorite/111 "Authorization: Bearer ${token}"
That's all I can offer to you, hope it will help you through your thoughts. If it won't you can still debug those two functions.
I had the same issue on ec2 amazon AMI Linux php7.2 apache2.4 but token get generated in apache request headers but was not visible in Laravel request header
so add this code in middleware this will work only on your server but may not work on localhost.
$headers = apache_request_headers();
$request->headers->set('Authorization', $headers['authorization']);
JWT middleware
try {
$headers = apache_request_headers(); //get header
$request->headers->set('Authorization', $headers['authorization']);// set header in request
$user = JWTAuth::parseToken()->authenticate();
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
return response()->json(['status' => 'Token is Invalid']);
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
return response()->json(['status' => 'Token is Expired']);
}else{
return response()->json(['status' => 'Authorization Token not found']);
}
}
Fixed it by adding RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] to the .htaccess, so the authorization header does not get discarded by Laravel. Might be useful to add this to the docs.
I faced an issue and got solution from adding below lines in
.htaccess
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
I add this in /etc/apache2/apache2.conf.
<Directory /var/www/html>
AllowOverride all
</Directory>
(remember to restart your apache)
Removing index.php from my url, resolve this problem.
/public/index.php/api/user/login -> /public/api/user/login

oAuthRequest returns empty array

I have created a Twitter application for a website. I would like to integrate the possibility of logging in/registering with Twitter. I am using php files as helpers, which are named TwitterOAuth.php and OAuth.php, respectively. When a user clicks on the Twitter button, he/she is redirected to a page called twitter.php, which has the following source-code:
<?php
/* Build TwitterOAuth object with client credentials. */
$connection = Common::twitter();
/* Get temporary credentials. */
$request_token = $connection->getRequestToken(App::env()->get('url'));
/* Save temporary credentials to session. */
$token = $request_token['oauth_token'];
$_SESSION['twitter'] = array('id' => $request_token['oauth_token'], 'token' => $request_token['oauth_token_secret']);
/* If last connection failed don't display authorization link. */
switch ($connection->http_code) {
case 200:
/* Build authorize URL and redirect user to Twitter. */
$url = $connection->getAuthorizeURL($token);
header('Location: ' . $url);
break;
default:
/* Show notification if something went wrong. */
var_dump($request_token);
echo 'Could not connect to Twitter. Refresh the page or try again later.';
}
The Common::twitter() function is as follows:
/**
* Returns the twitter OAuth service.
*
* #return TwitterOAuth
*/
public static function twitter() {
if (!self::$tw) {
if ((User::isLoggedIn()) && (User::current()->hasTwitterAccount())) {
self::$tw = new TwitterOAuth(
App::env()->get('twitter', 'consumerKey'),
App::env()->get('twitter', 'consumerSecret'),
App::CurrentUser()->getTwitterId(),
App::CurrentUser()->getTwitterUserAccessToken()
);
} else {
self::$tw = new TwitterOAuth(
App::env()->get('twitter', 'consumerKey'),
App::env()->get('twitter', 'consumerSecret')
);
}
}
return self::$tw;
}
In the scenario I am testing with, the else branch is executed. However, I get an exception:
Exception 'PHPErrorException' with message 'Notice [8] Undefined
index: oauth_token Error on line 81 in file ...\lib\TwitterOAuth.php
The function where the problem occurs is as follows:
/**
* Get a request_token from Twitter
*
* #returns a key/value array containing oauth_token and oauth_token_secret
*/
function getRequestToken($oauth_callback) {
$parameters = array();
$parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
return $token;
}
The problem is that OAuthUtil::parse_parameters($request) returns an empty array. This is happening, because $request is false, however, $this->requestTokenURL is https://api.twitter.com/oauth/request_token, $parameters has an oauth_callback, which holds the callback URL defined in the Twitter application. What could be the cause of this issue?
EDIT:
Source of `$this->oAuthRequest`:
/**
* Format and sign an OAuth / API request
*/
function oAuthRequest($url, $method, $parameters) {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
$request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
return $this->http($request->to_url(), 'GET');
default:
return $this->http($request->get_normalized_http_url(), $method, $request->to_postdata());
}
}
This method is inside of TwitterOAuth.php.
In OAuth.php there was a code chunk in the get_normalized_http_url method, namely:
$port = #$parts['port'];
This caused some errors, so I have fixed it like this:
$port = (array_key_exists('port', $parts) ? $parts['port'] : 80);
However, apparently port number 80 was the problem and after I changed the chunk to this:
$port = (array_key_exists('port', $parts) ? $parts['port'] : 443);
it worked like a spell.

Laravel Token Signature could not be verified

I'm using Laravel/Lumen as an API for the backend of a webapp and run into a hiccup.
In an example I have a route that does not need the user to be authenticated. But I do want to check in the routes controller if the user visiting has a valid token.
So I wrote the following:
if ($tokenFetch = JWTAuth::parseToken()->authenticate()) {
$token = str_replace("Bearer ", "", $request->header('Authorization'));
} else {
$token = '';
}
I believe the above will check the Bearer token is valid else it will return a blank variable.
The following is my entire Controller.
public function show($url, Request $request)
{
if ($tokenFetch = JWTAuth::parseToken()->authenticate()) {
$token = str_replace("Bearer ", "", $request->header('Authorization'));
} else {
$token = 'book';
}
return response()->json(['token' => $token]);
}
The Problem
If I a pass in a valid Token Bearer, it returns the token but if I pass in an invalid one I get the following error:
TokenInvalidException in NamshiAdapter.php line 62:
Token Signature could not be verified.
If I don't pass a token at all:
JWTException in JWTAuth.php line 195:
The token could not be parsed from the request
Is there a way to check if a token is passed and if it has then check if its valid, but also if one has not been passed then return a blank return?
You can wrap it inside try/catch block
public function show($url, Request $request)
{
try {
$tokenFetch = JWTAuth::parseToken()->authenticate())
$token = str_replace("Bearer ", "", $request->header('Authorization'));
}catch(\Tymon\JWTAuth\Exceptions\JWTException $e){//general JWT exception
$token = 'book';
}
return response()->json(['token' => $token]);
}
There are few exceptions that you might want to handle separately (jwt-auth/Exceptions)
Also as you're using laravel 5 you can global handling for JWT exceptions ,not recommended in this case but you should know of this option and choose yourself. app/Exceptions/Handler.php and inside render method add [at the top]
if ($e instanceof \Tymon\JWTAuth\Exceptions\JWTException) {
//what happen when JWT exception occurs
}
Yes it's possible to achieve what you want.
Check if a token is passed:
If you check in the documentation of parseToken you'll see that the algorithm to check if we pass a token is:
if (! $token = $this->parseAuthHeader($header, $method)) {
if (! $token = $this->request->query($query, false)) {
}
}
// which it will be in your case:
$hasToken = true;
$header = $request->headers->get('authorization');
if (! starts_with(strtolower('authorization'), 'bearer')) {
if (! $request->query('token', false)) {
$hasToken = false;
}
}
Check if a token is valid:
Please note that the NamshiAdapter use the Namshi\JOSE package so read the documentation here.
In NamshiAdapter.php as you can see the line who rise your error are:
if (! $jws->verify($this->secret, $this->algo)) {
throw new TokenInvalidException('Token Signature could not be verified.');
}
// in your case:
// + try to var_dump $this->secret, $this->algo
// + use Namshi\JOSE\JWS
// if you var_dump
$jsw = new JWS(['typ' => 'JWT', 'alg' => $algo]);
$jws = $this->jws->load($token, false);
// if you want to follow the documentation of Namshi\JOSE
$jws = JWS::load($tokenString, false, $encoder, 'SecLib');
// again var_dump for $this->secret, $this->algo
$isValidToken = ($jws->verify($this->secret, $this->algo));

Categories