Trying to get the header authorization key in controller for making an API. Request is making from fiddler.
$headers = apache_request_headers();
And the $header contains an array.
Array
(
[User-Agent] => Fiddler
[Host] => localhost:8000
[Content-Length] => 102
[Authorization] => TestKey
)
If am trying like this to fetch the Authorization , its throwing error.
$header['Authorization]
Error :
Undefined index: Authorization
Tried many ways to get the authorization, but not working anything. Is there any way to fetch this?
To get headers from the request you should use the Request class
public function yourControllerFunction(\Illuminate\Http\Request $request)
{
$header = $request->header('Authorization');
// do some stuff
}
See https://laravel.com/api/5.5/Illuminate/Http/Request.html#method_header
Though it's an old topic, it might be useful for somebody...
In new Laravel versions, it's possible to get bearer Authorization token directly by calling Illuminate\Http\Request's bearerToken() method:
Auth::viaRequest('costom-token', function (Request $request) {
$token = $request->bearerToken();
// ...
});
Or directly from a controller:
public function index(Request $request) {
Log::info($request->bearerToken());
// ...
}
If you use a specific package like "JWT" or "sanctum" you can use their own middleware to retrieve user information.
Also, Laravel provides many ways to get the authorization key like :
$request->bearerToken(); to get only token without 'Bearer' word the result will be like 44|9rJp2TWvTTpWy535S1Rq2DF0AEmYbEotwydkYCZ3.
$request->header('Authorization'); to get the full key like Bearer 44|9rJp2TWvTTpWy535S1Rq2DF0AEmYbEotwydkYCZ3
P.S. you can use request() shortcut instead of using $request variable
You can try install the jwt(JSON Web Token Authentication for Laravel & Lumen) http://jwt-auth.com via composer.
And create a middleware that verify if exists yours token key in the header request.
After to install jwt, Your middleware it could be as follows
<?php
namespace App\Http\Middleware;
use Closure;
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
class VerifyJWTToken
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try {
$user = JWTAuth::toUser($request->header('token'));
} catch (JWTException $e) {
if ($e instanceof TokenExpiredException) {
return response()->json([
'error' => 'token_expired',
'code' => $e->getStatusCode()
], $e->getStatusCode());
}
else if($e instanceof TokenInvalidException){
return response()->json([
'error' => "token_invalid",
'code' => $e->getStatusCode()
], $e->getStatusCode());
}
else {
return response()->json([
'error' => 'Token is required',
'code' => $e->getStatusCode(),
], $e->getStatusCode());
}
}
return $next($request);
}
}
I used token for example for a header key, but you can name it, as you like.
Then you could use this on any controller
There is a simple way to get headers in any file or controller with $_SERVER.
print_r($_SERVER); // check your header here
then, you can simply get your header:
$AuthToken = $_SERVER['HTTP_AUTHORIZATION'];
Related
I'm currently working on a Laravel 8 project, I have two projects:
A Laravel 8 project used as an API, it exposes some endpoints
A Laravel Lumen 8 project which runs on it's own domain.
Both have Cors enabled, and both run on the same domain, I'm having issues with Guzzle in my Lumen project connecting to an endpoint that exists on my Laravel API, here's the scenario and request flow:
A request comes in to: /hub/microservice/fudge-api-reports on the Laravel api, this is it's controller method:
/**
* Route the microservice
*
* #param \Illuminate\Http\Request $request
* #return \Illuminate\Http\Response
*/
public function microservice(Request $request, $service)
{
$validator = Validator::make($request->all(), [
'endpoint' => 'required|string',
'method' => 'required|string|in:post,get',
'data' => 'nullable|array'
]);
if ($validator->fails()) {
return response()->json([
'message' => "One or more fields has been missed or is invalid.",
'errors' => $validator->messages()
], 400);
}
$microservice = Microservice::where('microservice', $service)->first();
if (!$microservice) {
return response()->json([
'message' => "The microservice you're trying to access is invalid or doesn't exist"
], 404);
}
// auth
$auth = 'Bearer ' . $microservice->hub_access_token;
// the url
$url = $microservice->hub_domain . $request->input('endpoint');
// define how to communicate with the microservice
if ($request->input('method') == 'post') {
if ($microservice->hub_access_token) {
$response = Http::timeout(60)->withHeaders([
'Authorization' => $auth
])->post($url, $request->input('data'));
} else {
$response = Http::timeout(60)->post($url, $request->input('data'));
}
} else {
if ($microservice->hub_access_token) {
$response = Http::timeout(60)->withHeaders([
'Authorization' => $auth
])->get($url);
} else {
$response = Http::timeout(60)->get($url);
}
}
// the response from the microservice
return response()->json($response->json(), $response->status());
}
The request then (based on the endpoint and method) goes to the microservice which runs on it's own domain, mine is: http://localhost:8001/, my URL would be a GET request, and would have an access token, so it makes it into the else statement above and into the first if, e.g: http://localhost:8001/api/reports?report=MyReport and has a token as the header.
The request comes into the fudge-api-reports Laravel Lumen project, and goes to a controller method, but first passes through my BeforeMiddleware where it performs a "log in" request back to the Laravel API to authenticate and check the abilities, this part of the middleware is:
<?php
namespace App\Http\Middleware;
use Closure;
use GuzzleHttp\Client;
class BeforeMiddleware
{
/**
* Request attributes
*
*/
public $attributes;
/**
* Get API url
*/
protected function getApiUrl()
{
return rtrim(config('fudge.fudge_api_domain'), '/');
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $ability)
{
$api = $this->getApiUrl();
$token = $request->input('token');
if (!$token && $request->header('Authorization')) {
$token = explode(' ', $request->header('Authorization'))[1];
}
// TODO: this part appears to ALWAYS time out, despite
// http://localhost:8000/api/hub/login working just fine via Postman
$client = new Client([
'base_uri' => $api,
'timeout' => 5
]);
$res = $client->request('POST', '/api/hub/login', [
'token' => $token,
'ability' => "reports:$ability"
]);
// the response
$res = $res->json();
$hasAbility = isset($res['has_ability']) && !empty($res['has_ability']) ? $res['has_ability'] : false;
// not authorised
if (!$hasAbility) {
return response()->json([
'message' => "You aren't authorised"
], 200);
}
// add the hub's user to the request
$request->attributes->add(['has_ability' => $hasAbility]);
// Post-Middleware Action
return $next($request);
}
}
If the Hub log in is successful, then a has_ability is returned with the value of true back to the the middleware, which then goes through the controller method and finally returns the response back to the initial request of: /hub/microservice/fudge-api-reports
My issue
in my BeforeMiddleware my Guzzle POST request always fails, and never returns a response, it always seems to time out accessing my /api/hub/login endpoint that exists in my Laravel project.
It works perfectly fine through Postman, and Cors is enabled, why would this always timeout in the context of the Middleware, what am I missing?
I'm following this tutorial https://medium.com/tech-tajawal/jwt-authentication-for-lumen-5-6-2376fd38d454 to learn how to setup a tokenbased authentication for my lumen backend.
So far, everything worked fine. Only in the end it turns out that I cant get a token Oo
I don't think that theres anything wrong with my code.
When using RESTClient to do an http-Request to: "http://localhost:8080/users"
I get the desired "'error' : 'Token not provided'"
However, when I try to get the token as described in the tutorial, I get errors that the name and e-mail field aren't provided.
Here is my controller code (or the code from the tutorial) which evaluates the request for the transmitted name and email values:
<?php
namespace App\Http\Controllers;
use Validator;
use App\User;
use Firebase\JWT\JWT;
use Illuminate\Http\Request;
use Firebase\JWT\ExpiredException;
use Illuminate\Support\Facades\Hash;
use Laravel\Lumen\Routing\Controller as BaseController;
class AuthController extends BaseController
{
/**
* The request instance.
*
* #var \Illuminate\Http\Request
*/
private $request;
/**
* Create a new controller instance.
*
* #param \Illuminate\Http\Request $request
* #return void
*/
public function __construct(Request $request) {
$this->request = $request;
}
/**
* Create a new token.
*
* #param \App\User $user
* #return string
*/
protected function jwt(User $user) {
$payload = [
'iss' => "lumen-jwt", // Issuer of the token
'sub' => $user->id, // Subject of the token
'iat' => time(), // Time when JWT was issued.
'exp' => time() + 60*60 // Expiration time
];
// As you can see we are passing `JWT_SECRET` as the second parameter that will
// be used to decode the token in the future.
return JWT::encode($payload, env('JWT_SECRET'));
}
/**
* Authenticate a user and return the token if the provided credentials are correct.
*
* #param \App\User $user
* #return mixed
*/
public function authenticate(User $user) {
$this->validate($this->request, [
'email' => 'required|email',
'password' => 'required'
]);
// Find the user by email
$user = User::where('email', $this->request->input('email'))->first();
if (!$user) {
// You wil probably have some sort of helpers or whatever
// to make sure that you have the same response format for
// differents kind of responses. But let's return the
// below respose for now.
return response()->json([
'error' => 'Email does not exist.'
], 400);
}
// Verify the password and generate the token
if (Hash::check($this->request->input('password'), $user->password)) {
return response()->json([
'token' => $this->jwt($user)
], 200);
}
// Bad Request response
return response()->json([
'error' => 'Email or password is wrong.'
], 400);
}
}
And here is the code of the middleware:
<?php
namespace App\Http\Middleware;
use Closure;
use Exception;
use App\User;
use Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
class JwtMiddleware
{
public function handle($request, Closure $next, $guard = null)
{
$token = $request->get('token');
if(!$token) {
// Unauthorized response if token not there
return response()->json([
'error' => 'Token not provided.'
], 401);
}
try {
$credentials = JWT::decode($token, env('JWT_SECRET'), ['HS256']);
} catch(ExpiredException $e) {
return response()->json([
'error' => 'Provided token is expired.'
], 400);
} catch(Exception $e) {
return response()->json([
'error' => 'An error while decoding token.'
], 400);
}
$user = User::find($credentials->sub);
// Now let's put the user in the request class so that you can grab it from there
$request->auth = $user;
return $next($request);
}
}
Which I have registered in bootstrap/app.php:
$app->routeMiddleware([
'jwt.auth' => App\Http\Middleware\JwTMiddleware::class,
]);
But my JSON Payload looks like this so it SHOULD work.
[
{
"email": "ibeier#hotmail.com,
"name": "Eusebio Dach",
}
]
However I must admit that the tutorial in this section was a bit lacking in the details.
Here is that (last) part which I cant get to work:
"
To make this privious request successful we need to provide the token as query parameter. We can get the token by hitting the following route http://localhost:8000/auth/login inside Postman application. Now that you have the token open the priviously failed request and this time in the Params section create a key token and set it’s value to the token that you received in previous request. And after that send the request to http://localhost/users route and you will get the users list in response.
(Screenshot with userdata, see tutorial)
And that about wraps it up. If you have any questions feel free to leave your comments below.
"
I'm not sure whether I provided the correct request data and if I have set all headers correctly.
Maybe someone with a bit more knowledge in this can help me understanding what I am missing.
I already tried looking into other tutorials but these take different jwt extensions for their lumen and also seem to take other approaches in their controller code etc..
I'd very much like to follow this tutorial since its approach seemed pretty straight-forward to me.
It would also help if someone could tell me that the tutorial worked for them, and maybe also tell me what they did in addition to the instructions provided through the tutorial.
Because then I would just do the tutorial again and see if I can get it working.
Now, I'm using api.php route for requests from Axios on VueJS , And I need to logout from Auth::guard('web')->logout(); command but, at the moment, I cannot do this.
routes/api.php
Route::group([ 'prefix' => 'v1/auth', 'middleware' => 'jwt'], function () { //
Route::get('me', 'Auth\UserController#me');
Route::get('gg', 'Auth\UserController#test');
});
app/Http/sMiddleware/JwtMiddleware.php
<?php
namespace App\Http\Middleware;
use Closure;
use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;
use Tymon\JWTAuth\Exceptions\JWTException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Facades\JWTAuth;
use Illuminate\Support\Facades\Auth;
class RefreshToken extends BaseMiddleware
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
try
{
if (! $user = JWTAuth::toUser(JWTAuth::getToken()))
{
return response()->json([
'code' => 101, // means auth error in the api,
'response' => 'not authenticate' // nothing to show
]);
}
}
catch (TokenExpiredException $e)
{
// If the token is expired, then it will be refreshed and added to the headers
try
{
$refreshed = JWTAuth::refresh(JWTAuth::getToken());
header('Authorization: Bearer ' . $refreshed);
}
catch (JWTException $e)
{
return response()->json([
'code' => 103, // means not refreshable
'response' => 'token jwt exception' // nothing to show
]);
}
}
catch (JWTException $e)
{
Auth::guard('web')->logout(); // here
return response()->json([
'code' => 101, // means auth error in the api,
'response' => 'jwterror' // nothing to show
]);
}
return $next($request);
}
}
But when i migrated from api.php to web.php. I can use Axios to post for logout
Please, tell me how to use Auth::logout in api route file.
Sorry I'm not good at english.
Logout is implemented with the session driver, and unlike there web guard, the api guard is using a token driver not session driver.
Basically, the user is not logged into the API, but WEB part of your application.
In the api; find a way to invalidate/expire the token so that the user with that token can no longer access the api resources.
try {
JWTAuth::invalidate($request->input('token'));
return response()->json(['success' => true, 'message'=> "You have successfully logged out."]);
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['success' => false, 'error' => 'Failed to logout, please try again.'], 500);
}
Web logout
Session Logout
I'm using Laravel 5.3 & Passport.
When using Postman to test any route I have set in api.php file it always returns the login page. Here is an example of my testing route:
Route::get('/getKey', function() {
return 'hello';
})->middleware('client_credentials');
Postman params:
Accept application/json
Authorization Bearer <then my key>
I have set middleware to 'auth:api' per another solution I found while searching for the answer.
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('auth:api')
->namespace($this->namespace)
->group(base_path('routes/api.php'));
}
I've tried just about every solution that has worked for others but still no luck. Any suggestions would be much appreciated.
UPDATE
So I finally got something to work. I created a consumer app and created a few test functions. I was able to consume the api, with verification of token. However, hitting this Route no longer returns my login page, but instead now returns nothing. So its still not working for whatever reason.
Route::get('/user', function (Request $request) {
return $request->user();
})->middleware('client_credentials');
The redirection to the defined login route is occurring in the app\Exceptions\Handler.php class.
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->expectsJson()) {
return response()->json(['error' => 'Unauthenticated.'], 401);
}
return redirect()->guest(route('login'));
}
The function tries to detect whether it is being called from an API (it which case it returns a 401 Unauthorized reponse with JSON message) and if not it will redirect to the login page according to the comments it
Converts an authentication exception into an unauthenticated response
To resolve the issue in postman, on the request click on the Headers tab and add:
key: Accept
value: application/json
I'm pretty new to this so am not sure whether this is a header we should be adding when testing all API calls with Postman or just a nusience with how this laravel method is setup.
Anyway this would solve your issue with being redirected to the login page, however it's a sign your underlying authentication isn't working
You need to add Authorization: Bearer YOUR_TOKEN to your every request's header. Also, for latest version Laravel 5.5 or above. You need to add Accept: application/json to request header too.
Add this code on Headers on postman.
key Value
Accept application/json
It is coded to check whether the request comes from Ajax. In that case you will receive the following json if authentication fails:
{
"error": "Unauthenticated."
}
Otherwise it will assume you are using a browser and it will redirect to Html login page for authentication.
You can add the following header to your request to simulate an Ajax request:
X-Requested-With = XMLHttpRequest
From laravel 5.8 till the current 6.0 version there is a a middleware located at the app/http/Middleware which is \App\Http\Middleware\Authenticate, it has a method
redirectTo
with the code
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
Re-write this to
protected function redirectTo($request)
{
if (! ($request->expectsJson() || collect($request->route()->middleware())->contains('api'))) {
return route('login');
}
}
What this code does is to return a Illuminate\Routing\Redirector instance and sets it as the \Illuminate\Auth\AuthenticationException $redirectTo parameter . which is passed to \App\Exceptions#render by laravel.
At the render function you can write a logic to catch an \Illuminate\Auth\AuthenticationException exception.
Here is my own implementation
/**
* Render an exception into an HTTP response.
*
* #param \Illuminate\Http\Request $request
* #param \Exception $exception
* #return \Illuminate\Http\Response
*/
public function render($request, Exception $exception)
{
/**
* Render an Authentification exception when user trying to viditing a route or
* Perform an action is not properly authenticated
*/
if ($exception instanceof \Illuminate\Auth\AuthenticationException) {
return $this->unauthenticated($request,$exception);
}
}
/**
* Convert an authentication exception into a response.
*
* #param \Illuminate\Http\Request $request
* #param \Illuminate\Auth\AuthenticationException $exception
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function unauthenticated($request, \Illuminate\Auth\AuthenticationException $exception)
{
return $exception->redirectTo()
? redirect()->guest($exception->redirectTo())
: response()->json(['message' => $exception->getMessage()], 401);
}
As noted in many of the answers the reason this happens is indeed the code:
if (! $request->expectsJson()) {
return route('login');
}
in app\Http\Middleware\Authenticate.php
One way to solve this is to wrap your api requests in Middleware that adds 'Accept: application/json' to the header of those requests.
I got this idea from this article: https://medium.com/#martin.riedweg/laravel-5-7-api-authentification-with-laravel-passport-92b909e12528
Laravel version: 9.*
// app/Http/Middleware/Authenticate.php
protected function redirectTo($request)
{
return '/api/unauthenticated'; // You can name whatever route you want
}
// routes/api.php
Route::get('/unauthenticated', function () {
return response()->json(['message' => 'Unauthenticated.'], 403);
});
Hope this helps.
Happy coding!
If you don't want to enforce your api consumer to pass Accept - application/json in all the request headers you can add the following method in App\Exceptions\Handlers.php; to customize error response
//use Illuminate\Auth\AuthenticationException;
protected function unauthenticated($request, AuthenticationException $exception)
{
if ($request->is('api/*')) {
return response()->json(['error' => 'Invalid token', 'status' => false], 401);
}
return redirect()->guest(route('login'));
}
if you are using username instead of email for credential; insert this method at your User model:
function findForPassport($username) {
return $this->where('username', $username)->first();
}
I am using Oauth-server-laravel Authentication.
What i have done so far:
When i post wrong access_token to API that i have created in laravel it gives following response,
{
"error": "access_denied",
"error_description": "The resource owner or authorization server denied the request."
}
I have used oauth as middleware in route as follow,
Route::group(['namespace' => 'Modules\User\Http\Controllers', 'middleware' => 'oauth'], function () {
// Get User Profile Details
Route::post('getUserProfileDetail', 'UserController#getUserProfileDetail');
});
Problem:
If credentials are wrong then oauth automatically respond with it's default message and i want to customize that response messages,
I have half succeed in that if credentials are correct then it calls function that specifies in route and in that i am appending mre response that i want to send.
$response = $this->authorizer->issueAccessToken();
$code = 200; //Response OK
$response['result'] = 'success';
$response['user_id'] = $user['id'];
$response['email'] = $user['email'];
$response['name'] = $user['name'];
But i'm unable to send it when credentials are not correct because it can't call function and send it's default response to user.
I fall this types of problem to set custom message then this worked for me (a blog post I wrote about this article)]
So first create a middleware in app/Http/Middleware.My middleware name is OauthExceptionMiddleware
Then open
app/Http/kernel.php
and put this middleware instead of oauth2 previous middleware in $middleware array,like this
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
\App\Http\Middleware\OauthExceptionMiddleware::class,
];
Oauth2 Custom Exception Error Message
<?php
/**
* Created by PhpStorm.
* User: kingpabel
* Date: 3/23/16
* Time: 4:40 PM
*/
namespace app\Http\Middleware;
use Closure;
use League\OAuth2\Server\Exception\OAuthException;
class OauthExceptionMiddleware
{
public function handle($request, Closure $next)
{
try {
$response = $next($request);
// Was an exception thrown? If so and available catch in our middleware
if (isset($response->exception) && $response->exception) {
throw $response->exception;
}
return $response;
} catch (OAuthException $e) {
$data = [
// 'error' => $e->errorType,
// 'error_description' => $e->getMessage(),
'error' => 'Custom Error',
'error_description' => 'Custom Description',
];
return \Response::json($data, $e->httpStatusCode, $e->getHttpHeaders());
}
}
}