Laravel 5.2 Authentication Token - php

I'm trying to implement a Token based authentication in Laravel 5.2. What I've done is:
Routes.php
Route::group([
'middleware' => ['auth:api']
], function() {
Route::get('/api/reservation', 'apiController#get');
});
I've modified the User model and added the api_token field and added a new user with a random string via seeders:
Migration
Schema::table('users', function (Blueprint $table) {
$table->string('api_token', 60)->unique()->nullable()->default(null);
});
Seeder
User::create([
...,
'api_token' => str_random(60),
]);
Controller
class apiController extends Controller
{
function get(Request $request) {
return Reserva::all();
}
}
Then, In Postman I try to GET the url adding /api/reservation?api_token=xxxxx with the token I have in the database but I always get Unauthorized. Something weird is if I do a dd($this->auth) on the authentication middleware I get a TokenGuard object with the name='web'. Isn't It supposed to be api?
Maybe I'm misunderstanding something, can you guys give me a hint? Thank you

The auth:api middleware you are using uses Laravel Passport. You cannot use it if you want a custom token based authentication like you do where you create your own tokens.
If you want to use Passport, do this:
Keep your routes like that. The routes that require authentication must be inside of the auth:api middleware.
You can remove the api_token field of your users table. The $table->rememberToken() function in the migration is completely different than the API token you think of. In fact, tokens are not stored in the database at all. The token you see in the oauth_access_token table in the database is not the token you use for you HTTP requests.
Do NOT create a custom token like you do. Check that the login/password couple of the user is valid, generate a token and return it to the consumer of the API like that:
if (Auth::attempt(['login' => $req->login, 'password' => $req->password])) {
$user = Auth::user();
$token = $user->createToken('ToutelaBreizh')->accessToken;
return response()->json(['token' => $token],200);
}
Beware to place the login/register routes OUTSIDE of the auth:api middleware, otherwise you would need to give a token to a route that is supposed to give you this token - it makes no sense.
Next, make sure you send the token to the API in the Authorization header of the request, and not in the request parameters like you do. Add in Postman the following header:
Authorization: Bearer your_token_retrieved_before
Now you're pretty much done, you can use your API with Postman.

In case someone else face this problem. My problem was In the Authenticate middleware. I had an old version somehow.
I had this Authenticate middleware:
class Authenticate
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
And I solved it by changing it with this:
class Authenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return rediresct()->guest('login');
}
}
return $next($request);
}
}

Related

Returning response when user is unauthorised in Laravel

When a user is not authenticated, Laravel redirects all request made to routes using auth::api middleware to the default laravel login page. However since I am building the API only, I want to return a 401 unauthorised response to any app that will be consuming my api. This should be simple but for some reason, I haven't been able to do just that. Here's what my code looks like
public function show(User $user)
{
if ($user->id == auth()->user()->id) {
// do something here
} else {
return response()->json([ 'status_message' => 'Unauthorised'], 401);
}
}
public function update(Request $request, User $user)
{
if ($user->id == auth()->user()->id) {
// do something here
} else {
return response()->json(['status_message' => 'Unathorised'], 401);
}
}
When I hit the endpoints calling these methods and the user isn't authenticated, I am redirected to the default laravel login page. How can I override this default behavior?
Call api.auth middleware in your routes as follows
$api->group(['middleware' => 'api.auth'], function ($api){
//your routes
});
Create a file Authenticate.php inside Middleware folder
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Factory as Auth;
class Authenticate {
/**
* The authentication guard factory instance.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #return void
*/
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if ($this->auth->guard($guard)->guest()) {
return response('Unauthorized.', 401);
}
return $next($request);
}
}
and in kernel.php which is inside http folder include the following
protected $routeMiddleware = [
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
];
Problem solved guys. Here's how I did it, in the Handle.php file in app\Exceptions\ I added this code to the render function
if ($exception instanceof AuthenticationException) {
return response()->json(['status_message' => 'Unauthorised'], 401);
}
And that was it.
Note: this worked in laravel 5.8

Laravel Middleware based on a relation

In my Laravel application, administrators can be assigned to jobs, I also have super administrators that can do everything.
Administrators that are not super administrators should only be able to access jobs they are assigned to.
Let's use a rough route for illustration:
http://localhost:3000/jobs/{job}
http://localhost:3000/jobs/{job}/notes{note}
In this scenario {job} is an id acquired via route model binding and a note is attached to a job.
Administrators assigned to jobs are done so via the following relationship method:
/**
* Get the jobs that this admin has access to via the pivot table
*
* #return void
*/
public function jobs()
{
return $this->belongsToMany(JobPost::class, 'job_post_admin', 'admin_id', 'job_post_id');
}
So I can use $user->jobs
I want to be able to say the following - if you're a super admin you can go anywhere, if you're not, you should be restricted to what you've been assigned to.
So if a user only has access to http://localhost:3000/jobs/{1} and they go to http://localhost:3000/jobs/{2} they should be redirected.
I created a Middleware called Access
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::guard('admin')->user()) {
$user = Auth::guard('admin')->user();
if ($user->is_admin) {
return $next($request);
} else {
if ($user->jobs->contains($job)) {
return $next($request);
} else {
return response('You do not have sufficient priveledges to perform this action.', 403);
}
}
} else {
return redirect()->back();
}
}
However, I'm confused as to how I would get the job ID from the URL.
I have a working MiddleWare that looks like this:
<?php
namespace App\Http\Middleware;
use Closure;
use Auth;
class Access
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if (Auth::guard('admin')->user()) {
$user = Auth::guard('admin')->user();
$job = $request->vacancy;
if ($user->is_admin) {
return $next($request);
} else {
if ($user->jobs->contains($job)) {
return $next($request);
} else {
return response('You do not have sufficient priveledges to perform this action.', 403);
}
}
} else {
return redirect()->back();
}
}
}
I am now interested though, as without sounding dense I haven't really acknowledged authorization in Laravel, does it have benefits over Middleware?
Authentication is different to authorization. Laravel supports both.
You can do what you need using authorisation policies, there is no need for extra middleware.
First create a policy:
php artisan make:policy JobsPolicy --model=Job
This will make your boilerplate. Then you can add the actions:
class JobsPolicy {
use HandlesAuthorization;
//If you return true here the policy always succeeds
public function before(User $user, $ability) {
if ($user->is_admin) {
return true;
}
}
public function view(User $user, Job $job) {
return $user->jobs->contains($job);
}
public function create(User $user) {
return true; //Return true if the user can create jobs
}
public function update(User $user, Job $job) {
return $user->jobs->contains($job);
}
}
You need to also register your policy in the AuthServiceProvider in the $policies array:
protected $policies = [
Job::class => JobPolicy::class
];
Then you can add the already existing middleware e.g.:
Routes::get('jobs/{job}/notes/{note}', ...)->middleware("can:view,job");
This will ensure that the currently authenticated user can view the job specified by the route job parameter.
There's more information in the documentation

Laravel redirect()->intended() is not working in custom login controller

I have a custom login controller and its using return redirect()->intended(route('home')) , as per the documentation this should send the user redirect the user to the URL they were attempting to access before being intercepted by the authentication middleware.
But for my case every time it is redirecting to home route.I am sure i have done correctly or at least i think i have done correctly. Can anyone please tell me where i am doing this wrong ??
My logincontroller is like this:
public function __construct()
{
$this->middleware('guest');
}
public function login(Request $request)
{
$validatedData = $request->validate([
'email' => 'required|email|max:255',
'password' => 'required|max:255',
]);
try {
$response = HelperFunctions::fetchData('post', 'login/login', [
'loginId' => $request->get('email'),
'password' => md5($request->get('password'))
]);
if ($response['code'] == 200 && $response['success']) {
session([
'api_token' => $response['sessionId'],
'user_data' => $response['data']['profile']
]);
return redirect()->intended(route('home'));
} else {
return redirect()->back()->with('error', 'Please provide valid credentials');
}
} catch (\Exception $e) {
return redirect()->back()->with('error', 'Opps!! Something is wrong. We are trying to fix it');
}
My authentication checking middleware
class checkAuthentication
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
//check if user is logged in
if(Session::has('api_token') && Session::has('user_data')) {
return $next($request);
}
return redirect(route('login'));
}
}
I also tried to dd() Session::get('url.intended') but it returns empty .
I did tried looking for reference on google, laracast & stackoverflow i found some reference , but those did not help me. Can anyone please help me thank you.
Some of the reference i have checked & tried:
https://laracasts.com/discuss/channels/laravel/redirect-intended-not-working-after-login?page=1
Laravel 5 - redirect()->intended() after authentication not going to intended
https://laravel.io/forum/11-24-2014-problems-with-redirectintended-and-auth
The simplest way would be to redirect()->guest() in your Auth middleware instead of the redirect you currently have:
return redirect()->guest(route('login'));
Using the guest() method on the redirector is what adds the url.intended value to the session.
Alternatively, instead of returning from the middleware if they're not authenticated you could throw an AuthenticationException:
public function handle($request, Closure $next)
{
//check if user is logged in
if(Session::has('api_token') && Session::has('user_data')) {
return $next($request);
}
throw new AuthenticationException('Unauthenticated.');
}
This will allow you to take advantage of Laravel's ExceptionHandler if you need to. You will also need to add use Illuminate\Auth\AuthenticationException; to the top of your class.
I also tried to dd() Session::get('url.intended') but it returns empty .
In my case, this key existed in my session, but for some weird reason, something funny happens after redirect()->intended() returns the redirect response. Maybe the response is discarded in transit, or intercepted by some other caller on the stack.
What I did to work around it was to manually obtain the intended url from the session and hand it over to the redirect (instead of relying on the underlying protocols of intended()).
By adding this
redirect(Session::get('url.intended', $this->redirectPath()));
at the bottom of LoginController#showLoginResponse, once user is authenticated, Laravel is able to remember intended url and redirect to it
I have also faced this problem and the best way I come up with is using the authenticated method.
So Override the authenticated method in your controller and use the intended route there.
/**
* The user has been authenticated.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function authenticated(Request $request, $user)
{
if ($user->role == 'admin') {
return redirect()->intended('/admin');
} elseif ($user->role == 'user') {
return redirect()->intended('/');
}
}
also sometimes changing the RedirectIfAuthenticated middleware also helps.
so go to App\Http\Middleware\RedirectIfAuthenticated.php and edit it as well.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->check()) {
if ($request->user()->role == 'admin') {
return redirect()->intended('/admin');
} elseif ($request->user()->role == 'user') {
return redirect()->intended('/');
}
// return redirect('/home');
}
return $next($request);
}
}
Do this:
return redirect()->intended('home');
Instead of this:
return redirect()->intended(route('home'));

Laravel looses Session ( Authentication)

I have a problem with a specific route on Laravel. Every second time (and sometimes on the first time) when ill call a specific route, ill get an 401 error, returned from the Authentication Middleware.
File Middleware/Authenticate.php
class Authenticate
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param string|null $guard
* #return mixed
*/
public function handle($request, Closure $next, $guard = null)
{
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401); // THIS IS CALLED
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
From that route:
Route::group(['middleware' => 'auth'], function () {
.........
Route::get('events', 'TaskController#events');
});
TaskController.php
public function events(Request $request) {
$time_from = $request->start;
$time_to = $request->end;
$events = array();
$user_id = Auth::user()->id;
.....
return response()->json($events, 200);
}
All called from a JQuery $.get Request. I dont know why Laravel thinks i am a guest, and then looses the Session?
When you are doing ajax/api requests laravel thinks you're guest because session based authentications doesn't apply to this type of calls. Whenever you use auth middleware you'll get 401 on ajax, even if you're authenticated.
You need some type of token based authentication for the ajax/api calls, that sends Authentications header on requests and new middleware that handles authentications for it.

How can I use laravel 5.1 middleware parameter for multiple auth and protected routes?

I'm new to laravel 5.1.
How can I use middleware parameter to protect my admin routes from users ?
something like this:
Route::group(['middleware' => 'auth:admin'], function()
/* Admin only Routes*/
{
//////
});
I have a field "role" in my "users" table that get two values:
1 for admin
2 for users
In my application, users, have their protected route.
I don't want to use packages.
You can do something like this. Inject the Guard class, then use it to check the user. You dont need to pass the parameter really. Just name your middleware 'admin' or something. The following middleware will check if the current user's role is admin, and if not, redirect to another route. You can do whatever you prefer on failure.
<?php
namespace Portal\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Admin
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new filter instance.
*
* #param Guard $auth
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if($this->auth->user()->role != 'admin') {
return redirect()->route('not-an-admin');
}
return $next($request);
}
}
In case you do want to pass the parameter, you can do this:
public function handle($request, Closure $next, $role)
{
if($this->auth->user()->role != $role) {
return redirect()->route('roles-dont-match');
}
return $next($request);
}

Categories