laravel 8 -> authorization with jwt and postman - laravel

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

Related

Laravel Ajax login not set user Auth session

I am working in laravel framework. I have two type of login
Ajax base login
Simple form submit login
When I submit my simple login form then user login successfully, but when I login user via ajax request then I receive success response of login but when I submit another form after ajax login then it redirect me to simple login page for login because of user Auth not set properly in ajax login.
Here is middleware
public function handle($request, Closure $next, $guard = null) {
if (Auth::check()) {
return $next($request);
}
return redirect()->route('userLogin');
}
Controller method
public function ajaxPostLogin($inputs) {
try {
$inputs = $this->validateInputs($inputs, RulesHelper::$user_login, RulesHelper::$user_login_msg);
$inputs["user_type"] = 'user';
$response = $this->getObj(UserHelper::class)->ajaxUserLogin($inputs);
if (isset($response["success"])) {
echo json_encode(["success" => true, 'token' => csrf_token()]);
die;
} else {
echo json_encode(["success" => false, 'error' => $response]);
die;
}
} catch (\Exception $ex) {
echo json_encode(["success" => false, "message" => $ex->getMessage()]);
die;
}
}
I don't know that where I am doing mistake. Guide in a right way.
Thanks
In Laravel. After you Authenticate user via AJAX, dont echo any response. Echo only on errors. Not on success. This helped me as well.

Laravel passport create token and refresh token

I'm using Laravel and passport on my project.
In my project users can get token in two way.
First with username and password that its OK by passport.
Second with verification code that sent with SMS that its my problem.
I tried createToken() method but it will make a personal token without refresh token and I need to create token with refresh token and specify client id in a controller (without HTTP request).
$user = App\User::find(1);
// Creating a token without scopes...
$token = $user->createToken('Token Name')->accessToken;
How can I do this?
Create a new controller AccessTokenController that extends \Laravel\Passport\Http\Controllers\AccessTokenController
<?php
namespace App\Http\Controllers;
use App\User;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Psr\Http\Message\ServerRequestInterface;
use Response;
class AccessTokenController extends \Laravel\Passport\Http\Controllers\AccessTokenController
{
public function issueToken(ServerRequestInterface $request)
{
try {
//get username (default is :email)
$username = $request->getParsedBody()['username'];
//get user
$user = User::where('email', '=', $username)->firstOrFail();
//issuetoken
$tokenResponse = parent::issueToken($request);
//convert response to json string
$content = $tokenResponse->getBody()->__toString();
//convert json to array
$data = json_decode($content, true);
if(isset($data["error"]))
throw new OAuthServerException('The user credentials were incorrect.', 6, 'invalid_credentials', 401);
//add access token to user
$user = collect($user);
$user->put('access_token', $data['access_token']);
$user->put('expires_in', $data['expires_in']);
$user->put('refresh_token', $data['refresh_token']);
return Response::json(array($user));
}
catch (ModelNotFoundException $e) { // email notfound
//return error message
}
catch (OAuthServerException $e) { //password not correct..token not granted
//return error message
}
catch (Exception $e) {
////return error message
}
}
}
You can generate client_id by using following command
php artisan passport:client
I found a package and I'm using that
https://github.com/qiutuleng/laravel-passport-phone-verification-code-grant
It's good.

How to add status in default response passport

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

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 and jwt-auth - how to check if the user is logged in

I have set up Laravel with jwt (using jwt-auth). In my Kernel.php - $routeMiddleware I have added :
'jwt.auth' => \Tymon\JWTAuth\Middleware\GetUserFromToken::class,
'jwt.refresh' => \Tymon\JWTAuth\Middleware\RefreshToken::class
As I understand it 'jwt.refresh' will automatically refresh / create a new token for the user for each request.
In my single page ajax app I need to check if the user is logged in so I have added a route that calls this function:
public function isAuthenticated() {
$token = JWTAuth::getToken();
if(!$token){
throw new JWTException('Token not provided');
}
try{
$token = JWTAuth::refresh($token);
}catch(TokenInvalidException $e){
throw new AccessDeniedHttpException('The token is invalid');
}
return $this->response->withArray(['token'=>$token]);
}
The problem is that when isAuthenticated() is called the JWTAuth::refresh($token) call fails.
I guess it has something to do with that the token is refreshed.
What I want to do is to return true if the client's token is valid.
Is there a way to do this?
Removing 'jwt-refresh' seems to not solve the issue for us.
Thank you in advance!
My first observation is, where is the token stored? Is it parsed with the request? Because I believe that if your app uses jwt with api, then each request should have a token to signify a logged in user so something like this would be helpful:
try {
if (! $token = JWTAuth::parseToken()) {
//throw an exception
}
} catch (Exception $e) {
if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenInvalidException){
//throw an exception
}else if ($e instanceof \Tymon\JWTAuth\Exceptions\TokenExpiredException){
//throw an exception
} else if ( $e instanceof \Tymon\JWTAuth\Exceptions\JWTException) {
//throw an exception
}else{
//throw an exception
}
}
If successfully parsed from the request, then:
$user = JWTAuth::toUser($token);
see: https://github.com/tymondesigns/jwt-auth/wiki/Authentication
With your example code, if the token is not set - nothing is retrieved. However, if you want session based authentication, why not use the default authentication from Laravel.
Hope this helps :)
You can get the current user related to token and check if not null:
$user = JWTAuth::setToken($token)->toUser();
if($user == null){
abort(401);
}

Resources