Can anyone help me to handle API route with optional Auth check? I have an API ROUTE 'apply-coupon' which can be used by both 'Guest' and 'Logged In' user. I need to get User ID in controller if the user is logged in.
Front end - Is developed with React JS, on 'Apply Coupon' button click, an API is calling as -
API end point - https://example.com/api/apply-coupon
Request payload - {coupon : 'my-coupon-code', shop_id : '1', subtotal : '150'}
Note : No token is passed in the header, since 'apply coupon' feature is available for guest user as well.
Expected output :- When user clicks on 'Apply Coupon' it should return User ID, if user id logged in.
Following is my route in api.php file -
Route::post('/apply-coupon', [
'uses' => 'CouponController#applyCoupon',
]);
Update :- And the default guard is -
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
I have tried
if(Auth::user()) {
dd(Auth::user());
}
and
auth()->guard('api')->user()
But nothing worked. Thanks!!
I don't think you can "optionaly" protect routes. What I think you could do is check in your controller if the user is authenticated and if it is, then get its ID. Of course the route should be unprotected
use Illuminate\Support\Facades\Auth;
...
function applyCoupon() {
if (Auth::check()) {
// The user is logged in...
// return Cupon + Auth::id()
} else {
// Not logged in code
// return Cupon
}
}
docs
Add one middleware ValidateUser.php
namespace App\Http\Middleware;
use Closure;
use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Exceptions\TokenExpiredException;
use Tymon\JWTAuth\Exceptions\TokenInvalidException;
use Tymon\JWTAuth\Facades\JWTAuth;
class ValidateUser
{
public function handle($request, Closure $next)
{
if (! $request->bearerToken()) {
return $next($request);
}
return $this->validateToken($request, $next);
}
private function validateToken($request, Closure $next)
{
try {
if (! JWTAuth::parseToken()->authenticate()) {
throw new \Exception('Unable to find the account');
}
} catch (TokenExpiredException $e) {
throw new \Exception('Token has been expired');
} catch (TokenInvalidException $e) {
throw new \Exception('Token is not valid');
} catch (JWTException $e) {
throw new \Exception('Unable to parse the token');
}
return $next($request);
}
}
In your http/kernel.php in
$routeMiddleware
add
'api.auth.validate' => \App\Http\Middleware\ValidateUser::class
Now in your api.php you can use middleware to check it something like:
middleware(['api.auth.validate'])
Related
I am building a ReactJS App with a Laravel 8.9.0 backend api. I am using the Laravel Auth functionality that creates a token and passes it to my front end app. I am able to log-in and create a token properly with a hash password etc. What I am not able to do is "Check Login" with the is_login method shown below in the controller. The Auth::check() is always failing, what am I doing wrong? Below are my controllers and routes api.php file. Please help!
Login Controller:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Models\Users;
class LoginController extends Controller
{
public function login(Request $request) {
$login = $request->validate([
'email' => 'required:string',
'password' => 'required:string'
]);
if(!Auth::attempt($login)) {
return response([
'message' => 'Invalid Credentials'
]);
}
$accessToken = Auth::user()
->createToken('authToken')
->accessToken;
return response([
'user' => Auth::user(),
'access_token' => $accessToken
]);
}
public function is_login()
{
$is_login = false;
if (Auth::check()) {
$is_login = true;
}
return response()->json(['is_login' => $is_login]);
}
}
Routes api.php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
Route::post('/login', 'App\Http\Controllers\LoginController#login');
Route::get('/login-check', 'App\Http\Controllers\LoginController#is_login');
To fix this problem you must pass the token into the header with every request:
In my ReactJS I did this on componentDidMount()
componentDidMount() {
try {
const config = {
headers: {
Authorization: `Bearer ${mytoken}`
}
};
axios.get('/api/login-check',
{ key: "value" },
{ headers: config }
).then(
console.log('succ')
).catch(
console.log('err')
);
} catch (err) {
console.log('Error in submission', err)
}
}
and in my api I added the middleware to the check:
Route::middleware('auth:api')->get('/login-check', 'App\Http\Controllers\LoginController#is_login');
We have 2 auth middlewares applied to specific routes, 'external_token' and 'auth:api'. When an external bearer token is presented we inspect it, and if all the values are good we consider the user authorized to access the requested url.
How do we process all other middlewares except passport auth?
public function handle(Request $request, Closure $next)
{
$token = $request->header('Bearer');
try {
list($JWTHeader, $JWTPayload) = JWT::verify($token, JWT::TYPE_ID_EXTERNAL);
$this->user = User::where('external_id', $JWTPayload['external_id'])->first();
// Can we just set $this->user and process all other middlewares except auth?
} catch (Exception $e) {
Log::debug($e);
}
$response = $next($request);
return $response;
}
Well, one thing you could do would be to set the user on the api guard, so when the auth middleware runs, it'll find the user you provided. You would have to ensure that your external_token middleware runs first.
auth()->guard('api')->setUser($this->user);
Another option would be to convert your external_token middleware into a Laravel auth guard so that you can use the built-in auth functionality. Then, you can protect your route with auth:api,external_token, and the auth will pass if any one of the specified guards is successful.
The simplest example would be a closure request guard.
In your AuthServiceProvider::boot() method:
// don't forget your "use" statements for all these classes
public function boot()
{
// ...
Auth::viaRequest('external_token_driver', function ($request) {
$token = $request->header('Bearer');
try {
list($JWTHeader, $JWTPayload) = JWT::verify($token, JWT::TYPE_ID_EXTERNAL);
return User::where('external_id', $JWTPayload['external_id'])->first();
} catch (Exception $e) {
Log::debug($e);
}
return null;
});
}
In your auth.php config:
'guards' => [
// web, api, etc...
'external_token' => [
'driver' => 'external_token_driver',
],
],
NB: all untested.
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 am trying to authorize user from android device. I integrated the JWT package in my project and made the token based authorization. Here is how my Controller looks like:
class AdminLoginController extends Controller
{
public function __construct()
{
$this->middleware('jwt.auth',['except'=>['authenticate']]);
}
public function authenticate(Request $request)
{
$credentials = $request->only('email', 'password');
try {
// 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
return response()->json(['error' => 'could_not_create_token'], 500);
}
// if no errors are encountered we can return a JWT
return response()->json(compact('token'));
}
With this i am being to get token for the valid user request but i need to pass the parameters from the parameters section in postman.I have the put on the picture here . But if i try to post parameters from the body as a form data it says invalid credentials for the same user. How can i solve it. Any kinds of help are appreciated. Thank you.
Fisrt step check:
dd($credentials);
The ouput should be like this:
array:2 [
"email" => "kajal#gmail.com"
"password" => "kajal1"
]
Second step check:
dd($token);
And show the ouput.
I'm new to laravel 5. I just want know how to protect routes from invalid URL input. An example of an invalid input URL is shown below:
http://localhost:90/csadcsvs
If someone enters invalid URL from my laravel project, I just want to redirect to login page.
Works by adding
return redirect()->guest('auth/login')
instead of the below in app/Exceptions/Handler.php
return parent::render($request, $e)
It is used as shown below:
public function render($request, Exception $e)
{
if ($e instanceof ModelNotFoundException) {
$e = new NotFoundHttpException($e->getMessage(), $e);
}
return redirect()->guest('auth/login');
}
Route::get('/{any}','Pagecontroller#index');
any url which is not declared in the route will be accepted by {any} and the will be controlled back to index.
Hey create middleware using this LINK this is my auth middleware
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
in here if you use this middle ware like this without sign in you can't access your url csadcsvsyou will redirect to login page this is the way protect url in laravel
Route::get('csadcsvs',['middleware' => 'auth', function () {
}]);
hope this will help you :)