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

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

Related

Symfony Oauth2 with gard

I am trying to configure guard with an OAuth 2 connection.
I am trying to do this with a redirection in the getCredentials function to the Microsoft login website but I can't make it work. I don't know how I can make it worked.
It seems there is no redirection possible in this function.
public function getCredentials(Request $request)
{
$provider = new Microsoft([
'clientId' => '0000000032624',
'clientSecret' => 'my-secret',
'redirectUri' => 'https://mysite/oauthlogin'
]);
if(!$request->query->has('code')){
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl();
$request->getSession()->set('oauth2state', $provider->getState());
//This doesn't work
return new RedirectResponse($authUrl);
// Check given state against previously stored one to mitigate CSRF attack
}elseif ( empty($request->query->get('state')) || ($request->query->get('state')!==$request->getSession()->get('oauth2state')) ){
return null;
}else{
// Try to get an access token (using the authorization code grant)
$token = $provider->getAccessToken('authorization_code', [
'code' => $request->query->get('code')
]);
try {
//when log with microsoft, check if user is allowed
// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($token);
} catch (Exception $e) {
// Failed to get user details
}
}
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
return $userProvider->loadUserByUsername($user->getEmail());
}
public function checkCredentials($credentials, UserInterface $user)
{
// check credentials - e.g. make sure the password is valid
// no credential check is needed in this case
// return true to cause authentication success
return true;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
$url = $this->router->generate('homepage');
return new RedirectResponse($url);
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
$data = array(
'message' => strtr($exception->getMessageKey(), $exception->getMessageData())
// or to translate this message
// $this->translator->trans($exception->getMessageKey(), $exception->getMessageData())
);
$request->getSession()->set(Security::AUTHENTICATION_ERROR, $data);
$url = $this->router->generate('login');
return new RedirectResponse($url);
}
Function getCredentials() is not supposed to return a Response, it provide the credentials used in getUser().
In the getUser() documentation :
The credentials are the return value from getCredentials()
You may throw an AuthenticationException if you wish. If you return
null, then a UsernameNotFoundException is thrown for you.
In case of exception thrown, onAuthenticationFailure() is called and here you can return your RedirectResponse.
For more detailled informations, see the source code of the \Symfony\Component\Security\Guard\GuardAuthenticatorInterface which contains a lots of explanations in its methods.

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.

SLIM Framework Route Authentication v2 vs v3

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}]

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

Laravel JWT tokens are Invalid after refresh them in a authentication JWT approach

EDIT:
Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83
MY ORIGINAL QUESTION:
I'm implement with jwt-auth my protected resources that require an authenticated user with bellow code:
Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function() {
// Protected routes
});
When user 'sign in' on API an Authorization token is created, and sent on response Authorization header to client application that call the resource. So, client applications when intercept a Authorization token on header of any response, set a variable/session/whatever with this token value, to send again to API on next request.
The first request for a protected resource after 'login' works fine, but the next client application request to API with a refreshed token, gives the following error (API mount all responses in json format):
{
"error": "token_invalid"
}
What can be happen with refreshed tokens? My refresh token implementation (set as a after middleware) is wrong? Or isn't necessary to manually refresh all Authorization token that come with client apps requests?
UPDATE:
I update the jwt-auth RefreshToken middleware as propose here, but the token_invalid persist.
BUG:
I guess that I found what happens. Note that in the refresh method, old token is added to blacklist cache case enabled:
// Tymon\JWTAuth\JWTManager
public function refresh(Token $token)
{
$payload = $this->decode($token);
if ($this->blacklistEnabled) {
// invalidate old token
$this->blacklist->add($payload);
}
// return the new token
return $this->encode(
$this->payloadFactory->setRefreshFlow()->make([
'sub' => $payload['sub'],
'iat' => $payload['iat']
])
);
}
And note that in add to blacklist method the key is the jti param from old token payload:
// Tymon\JWTAuth\Blacklist
public function add(Payload $payload)
{
$exp = Utils::timestamp($payload['exp']);
// there is no need to add the token to the blacklist
// if the token has already expired
if ($exp->isPast()) {
return false;
}
// add a minute to abate potential overlap
$minutes = $exp->diffInMinutes(Utils::now()->subMinute());
$this->storage->add($payload['jti'], [], $minutes);
return true;
}
Thus, when has on blacklist method is called, the old token jti param is the same that the new, so the new token is in blacklist:
// Tymon\JWTAuth\Blacklist
public function has(Payload $payload)
{
return $this->storage->has($payload['jti']);
}
If you don't need the blacklist functionality just set to false on jwt.php configuration file. But I can't say if it expose to some security vulnerability.
Read the discussion about the bug at: https://github.com/tymondesigns/jwt-auth/issues/83
When I get this issue, the solution that I found to get my project working was to generate a new token with data from older token on each new request.
My solution, that works for me, is bad, ugly, and can generate more issues if you have many async requests and your API(or business core) server is slow.
For now is working, but I will investigate more this issue, cause after 0.5.3 version the issue continues.
E.g:
Request 1 (GET /login):
Some guest data on token
Request 2 (POST /login response):
User data merged with guest data on old token generating a new token
Procedural code example(you can do better =) ), you can run this on routes.php out of routes, I say that is ugly haha:
// ----------------------------------------------------------------
// AUTH TOKEN WORK
// ----------------------------------------------------------------
$authToken = null;
$getAuthToken = function() use ($authToken, $Response) {
if($authToken === null) {
$authToken = JWTAuth::parseToken();
}
return $authToken;
};
$getLoggedUser = function() use ($getAuthToken) {
return $getAuthToken()->authenticate();
};
$getAuthPayload = function() use ($getAuthToken) {
try {
return $getAuthToken()->getPayload();
} catch (Exception $e) {
return [];
}
};
$mountAuthPayload = function($customPayload) use ($getLoggedUser, $getAuthPayload) {
$currentPayload = [];
try {
$currentAuthPayload = $getAuthPayload();
if(count($currentAuthPayload)) {
$currentPayload = $currentAuthPayload->toArray();
}
try {
if($user = $getLoggedUser()) {
$currentPayload['user'] = $user;
}
$currentPayload['isGuest'] = false;
} catch (Exception $e) {
// is guest
}
} catch(Exception $e) {
// Impossible to parse token
}
foreach ($customPayload as $key => $value) {
$currentPayload[$key] = $value;
}
return $currentPayload;
};
// ----------------------------------------------------------------
// AUTH TOKEN PAYLOAD
// ----------------------------------------------------------------
try {
$getLoggedUser();
$payload = ['isGuest' => false];
} catch (Exception $e) {
$payload = ['isGuest' => true];
}
try {
$payload = $mountAuthPayload($payload);
} catch (Exception $e) {
// Make nothing cause token is invalid, expired, etc., or not exists.
// Like a guest session. Create a token without user data.
}
Some route(simple example to save user mobile device):
Route::group(['middleware' => ['before' => 'jwt.auth', 'after' => 'jwt.refresh']], function () use ($getLoggedUser, $mountAuthPayload) {
Route::post('/session/device', function () use ($Response, $getLoggedUser, $mountAuthPayload) {
$Response = new \Illuminate\Http\Response();
$user = $getLoggedUser();
// code to save on database the user device from current "session"...
$payload = app('tymon.jwt.payload.factory')->make($mountAuthPayload(['device' => $user->device->last()->toArray()]));
$token = JWTAuth::encode($payload);
$Response->header('Authorization', 'Bearer ' . $token);
$responseContent = ['setted' => 'true'];
$Response->setContent($responseContent);
return $Response;
});
});

Categories