How to add status in default response passport - laravel

I am developing and application in web as well as App. For App's API I have use Passport in laravel. Using passport i can create token and using that token I verify user for other api access. But if token in invalid then it return Error message like "Unauthorized" without any status. Can any one tell me what to do if i want to add error status like 401 with message "Unauthorized" . Where I have to change in code so that my web code is not affect. I want json response like below with 2 fields in json.
status:401
message:Unauthorized

You could create a new exception handler middleware to catch these requests and modify the response it returns.
Example:
class oAuthExceptionHandler {
public function handle($request, Closure $next) {
try {
$response = $next($request);
if (isset($response->exception) && $response->exception) {
throw $response->exception;
}
return $response;
} catch (\Exception $e) {
return response()->json(array(
'result' => 0,
'msg' => $e->getMessage(),
), 401);
}
}
}
Then, in your app/Http/Kernel.php, name your middleware and add it into your api group:
protected $routeMiddleware = [
'oauth_exception' => oAuthExceptionHandler::class,
...
];
protected $middlewareGroups = [
...
'api' => [
'oauth_exception',
...
],
];

You can handle the api exception and format its response in app/Exceptions/Handler.php.
Here is the link that you can follow
Laravel API, how to properly handle errors

Related

laravel 8 -> authorization with jwt and postman

I'm building an API with Laravel8 and use postman , Also I use JWT for authentication and want to authorization for creating a new post , I want to be able to create a new post only when the user is an admin or author , so I create a middleware AdminLevelAuthorization and in kernel I added it as 'auth.level'
So I put this middleware for my route in api.php : Route::apiResource('posts' , PostController::class)->middleware('auth.level:admin,author');
In postman I logged in and saved my token, I just don't know how use this token for authorization
When I go to this route http://localhost:8000/api/posts in postman and in authorization part , bearer token type I enter my token in token field , so it says :
"You are unauthorized to access this resource"
I don't know I'm wrong or not in entering token or my middleware is the problem
This is my middleware :
class AdminLevelAuthorization
{
public function handle($request, Closure $next, ...$levels)
{
try {
//Access token from the request
$token = JWTAuth::parseToken();//Try authenticating user
$user = $token->authenticate();
} catch (TokenExpiredException $e) {
//Thrown if token has expired
return $this->unauthorized('Your token has expired. Please, login again.');
} catch (TokenInvalidException $e) {
//Thrown if token invalid
return $this->unauthorized('Your token is invalid. Please, login again.');
} catch (JWTException $e) {
//Thrown if token was not found in the request.
return $this->unauthorized('Please, attach a Bearer Token to your request');
}
//If user was authenticated successfully and user is in one of the acceptable levels, send to next request.
if ($user && in_array($user->levels, $levels)) {
return $next($request);
}
return $this->unauthorized();
}
private function unauthorized($message = null){
return response()->json([
'message' => $message ? $message : 'You are unauthorized to access this resource',
'success' => false
], 401);
}
}
Thank you for your help :)
the problem was in s for user->levels it must be $user->level

How to prevent an error on undefined routes in Laravel 5.5

I developed an API with Laravel 5.5. All is working fine.
But imagine that a user enter an "api" url directly in the browser (for example: api/books), then they will receive an error:
InvalidArgumentException
Route [login] not defined.
How to prevent this? I tried to add some routes in the routes/web.php file, but without success.
Or perhaps I should do nothing (there will be very few users who will do that)?
I found the answer here:
Laravel 5.5 change unauthenticated login redirect url
I only do that in the "app/Exceptions/Handler.php" file, I modified the function "render" like that :
public function render($request, Exception $exception)
{
// return parent::render($request, $exception);
// add dom
return redirect('/');
// or redirection with a json
/*
return response()->json(
[
'errors' => [
'status' => 401,
'message' => 'Unauthenticated',
]
], 401
);
*/
}
And it works fine. As the "Laravel" part will be used only as back end for APIs, it will be enough.

Laravel passport API return Exception as HTML instead of JSON response

My API has been built with Laravel 5.4 Passport, authorisation and issuing access tokens is working properly, but when dealing with a resource like below using Insomnia or Postman:
Authorization: Bearer [encrypted access token goes here which has only "manage-users" scope]
Content-Type: application/json
Accept: application/json
I send above request to this url:
http://apiendpoint.loc/api/users
which has been restricted access to this recourse to tokens which has this scopes
manage-users, test-scope-1
Route::get('/users', [
'as' => 'users',
'uses' => 'UsersController#index'
])->middleware(['auth:api', 'scopes:manage-users,test-scope-1']);
scopes have been defined in the:
AuthServiceProvider
Passport::tokensCan([
'manage-users' => 'testing',
'test-scope-1' => 'test scope',
'test-scope-2' => 'another test scope',
]);
protected $routeMiddleware = [
...,
...,
...,
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class
];
The token used to authorize this request has "manage-users" scope only, so I expected to get json response with unauthorized access 401 in addition to the required scope to access this resource which is "test-scope-1".
although I got a HttpException "Invalid scope(s) provided." as HTML response not json
Edit
Auth-Scaffolding is not installed.
After a lot of digging, I found a way to to work around the issue earlier in the exception handler like below:
public function render($request, Exception $exception)
{
// If the request wants JSON (AJAX doesn't always want JSON)
if ($request->wantsJson()) {
if($exception instanceof MissingScopeException){
// Define the response
$response = [
'errors' => 'Sorry, something went wrong.'
];
// If the app is in debug mode
if (config('app.debug')) {
// Add the exception class name, message and stack trace to response
//$response['exception'] = get_class($exception); // Reflection might be better here
$response['message'] = $exception->getMessage();
//$response['trace'] = $exception->getTrace();
}
// Default response of 401
$status = 403;//forbidden
// If this exception is an instance of HttpException
if ($this->isHttpException($exception)) {
// Grab the HTTP status code from the Exception
$status = $exception->getStatusCode();
}
// Return a JSON response with the response array and status code
return response()->json($response, $status);
}
}
return parent::render($request, $exception);
}
so I'll be able to catch the error early and return a json object as a response.

Lumen JWT send token with requests

Authentication is working, I have a few routes under auth middleware, Whenever i request it throws :
{
"message": "Failed to authenticate because of bad credentials or an invalid authorization header.",
"status_code": 401
}
How can i send the token with the request like :
Authorization bearer {{Long token}}
It works with `postman`, How can i send the token with request header, Or in any other best way.
Route :
$api->get('/categories', [
'uses' => 'App\Http\Controllers\CategoryController#index',
'as' => 'api.categories',
]);
Method :
public function index() {
$lessons = \App\Category::all();
$token = JWTAuth::getToken(); // $token have jwt token
return response()->json([
'data' => $lessons,
'code' => 200,
]);
}
The question was pretty vague to answer. Please be more specific from next time. From your comments i could finally realise that you want to consume the api from a mobile app.
You need to return the token generated for an user either during login or during registration or any other authentication method/route you have. The mobile app needs to read this response and store the token locally. Then the app needs to inject this token in the request header for every single request. That's the normal api token workflow.
The app should also be coded to read the error response from requests and if it returns errors for expired or invalid token, the app needs to clear the locally stored token and then request the user to login again to generate a fresh token.
you can use : https://github.com/tymondesigns/jwt-auth
requriment :
Laravel 4 or 5 (see compatibility table)
PHP 5.4 +
Steps:
1 : add below line in composer.json in require array
"tymon/jwt-auth": "0.5.*"
2 : run "composer update" in your terminal
3 : after this you have to register service provider
go to config/app.php
and add 'Tymon\JWTAuth\Providers\JWTAuthServiceProvider' this in provider array
and 'JWTAuth' => 'Tymon\JWTAuth\Facades\JWTAuth' , 'JWTFactory' => 'Tymon\JWTAuth\Facades\JWTFactory' this to aliases array
4 : publish pacakge :
"php artisan vendor:publis --provider="Tymon\JWTAuth\Providers\JWTAuthServiceProvider"
5 : generate secrate key in config file
'php artisan jwt:generate'
6 : for addition configuration : https://github.com/tymondesigns/jwt-auth/wiki/Configuration
Usage :
AuthenticateController.php
use JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;
class AuthenticateController extends Controller
{
public function authenticate(Request $request)
{
// grab credentials from the request
$credentials = $request->only('email', 'password');
try {
// attempt to verify the credentials and create a token for the user
if (! $token = JWTAuth::attempt($credentials)) {
return response()->json(['error' => 'invalid_credentials'], 401);
}
} catch (JWTException $e) {
// something went wrong whilst attempting to encode the token
return response()->json(['error' => 'could_not_create_token'], 500);
}
// all good so return the token
return response()->json(compact('token'));
}
}
You can also skip user authentication and just pass in a User object. e.g.
// grab some user
$user = User::first();
$token = JWTAuth::fromUser($user);
The above two methods also have a second parameter where you can pass an array of custom claims. e.g.
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
JWTAuth::attempt($credentials, $customClaims);
// or
JWTAuth::fromUser($user, $customClaims);
create token based on anything
$customClaims = ['foo' => 'bar', 'baz' => 'bob'];
$payload = JWTFactory::make($customClaims);
$token = JWTAuth::encode($payload);
d

Laravel Passport consuming own API fail

I'm building a SPA with Vue. My front-end and my back-end (Laravel) are in the same codebase. I want to approach my API (that is in my back-end) via the Laravel Passport Middleware CreateFreshApiToken. I'm approaching my sign in method in my AuthController via web.php.
My problem:
As soon as I'm successfully signed in via my sign in method I would expect that at this time Passport created the laravel_token cookie. This is not the case. The cookie is created after a page refresh. But as I said I'm building a SPA and that's why I don't want to have page refreshes.
What I want:
I want to sign in via my sign in method then use the Passport CreateFreshApiToken middleware. After that I want to use the (just created in the middleware) laravel_token cookie so that I can correctly and safely speak to my own API in my signed-in section of the SPA.
More information:
Kernel.php
// Code...
protected $middlewareGroups = [
'web' => [
// other middlewares...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
];
// Code...
AuthController.php
// Code...
public function login()
{
if (Auth::attempt(['email' => Input::get('email'), 'password' => Input::get('password')], true)) {
return response()->json([
'user' => Auth::user(),
'authenticated' => auth()->check(),
]);
}
return response()->json(['authenticated' => false], 401);
}
// Code...
Login.vue
// Code...
methods: {
login: function (event) {
event.preventDefault();
this.$http.post(BASE_URL + '/login', {
email: this.email,
password: this.password,
})
.then(function (response) {
localStorage.user_id = response.body.user.id;
router.push({
name: 'home'
});
});
},
},
// Code...
What goes wrong? This:
CreateFreshApiToken.php
// Code...
public function handle($request, Closure $next, $guard = null)
{
$this->guard = $guard;
$response = $next($request);
// I'm signed in at this point
if ($this->shouldReceiveFreshToken($request, $response)) { // returns false unless you refresh the page. That's why it won't create the laravel_token cookie
$response->withCookie($this->cookieFactory->make(
$request->user($this->guard)->getKey(), $request->session()->token()
));
}
return $response;
}
protected function shouldReceiveFreshToken($request, $response)
{
// both methods below return false
return $this->requestShouldReceiveFreshToken($request) &&
$this->responseShouldReceiveFreshToken($response);
}
protected function requestShouldReceiveFreshToken($request)
{
// $request->isMethod('GET') - returns false because it's a POST request
// $request->user($this->guard) - returns true as expected
return $request->isMethod('GET') && $request->user($this->guard);
}
protected function responseShouldReceiveFreshToken($response)
{
// $response instanceof Response - returns false
// ! $this->alreadyContainsToken($response) - returns false as expected
return $response instanceof Response &&
! $this->alreadyContainsToken($response);
}
// Code...
I assume it is possible what I want to achieve right? If yes, how?
I had the same issue, decided to stick to client_secret way. I guess it's not relevant for you now, but I've found 2 ways of receiving the laravel token without refresh:
1) sending dummy get request with axios or $http, whatever you use - token will get attached to response;
2) changing requestShouldReceiveFreshToken method in CreateFreshApiToken.php - replace return $request->isMethod('GET') && $request->user($this->guard); with return ($request->isMethod('GET') || $request->isMethod('POST')) && $request->user($this->guard);
function consumeOwnApi($uri, $method = 'GET', $parameters = array())
{
$req = \Illuminate\Http\Request::create($uri, $method, $parameters, $_COOKIE);
$req->headers->set('X-CSRF-TOKEN', app('request')->session()->token());
return app()->handle($req)->getData();
}

Resources