Authentication - Restricting access to Routes
Closed this issue · 4 comments
Hi
Thank you for the great starter app. I am working with this and note that I cannot see an example of a restricted API route. I have tested the 'middleware' => 'auth.basic'
which works as expected, but I am struggling to use token based authentication to restrict API routes with for example 'jwt.auth'
.
app \ routes.php
Route::group(array('prefix' => 'api/', 'middleware' => 'jwt.auth'), function () {
Route::resource('todo', 'TodoController');
});
app \ Kernal.php
protected $routeMiddleware = [
'auth' => 'Todo\Http\Middleware\Authenticate',
'auth.basic' => 'Illuminate\Auth\Middleware\AuthenticateWithBasicAuth',
'guest' => 'Todo\Http\Middleware\RedirectIfAuthenticated',
'jwt.auth' => 'Tymon\JWTAuth\Middleware\GetUserFromToken',
'jwt.refresh' => 'Tymon\JWTAuth\Middleware\RefreshToken',
];
However the default responses from jwt.auth are not handled by the AngularJS interceptor well. I plan on using my own middleware with the responses set out in a format which works with the Todo app, but any other ideas on approaching this would be gratefully received.
Many thanks
Tim
Hi Tim,
I have the same problem because I would like to create some restricted routes. Does your method works or not? Can you share here your solution, please?
Thanks a lot
Hi Giorgio
I haven't worked on this for a while, but I believe I got a prototype working. The logging off may need some work (I was not sure exactly how this works server side to remove the token). Hopefully I haven't forgotten any changes I made, but please let me know any ideas / thoughts.
On Angular:
I altered the $httpProvider.intercepters...
$httpProvider.interceptors.push(['$rootScope', '$q', '$localStorage',
function ($rootScope, $q, $localStorage) {
return {
request: function (config) {
config.params = config.params || {};
config.headers = config.headers || {};
if ($localStorage.token) {
config.headers.Authorization = 'Bearer ' + $localStorage.token;
config.params.token = $localStorage.token;
}
return config;
},
response: function (res) {
return res || $q.when(res);
},
'responseError': function(response) {
if(response.status === 401 || response.status === 400) {
//console.log("Not logged in");
// Handle unauthenticated user
$rootScope.$broadcast('unauthorized');
//$location.path('auth/login');
}
return $q.reject(response);
}
};
}
]);
MainController.js added a function to redirect to login form when API returns unauthorized (this may be better handled by hiding the current content and showing a login form rather than redirecting?). You must add $rootScope to the dependencies for the module.
$rootScope.$on('unauthorized', function() {
// Logged out on server so logout client side too (if not already)
$scope.logout();
// Load Login Form
$location.path('auth/login');
});
On Laravel
Add the new middleware file in App > Http > Middleware > SpirantJwtGetUserFromToken.php
<?php
namespace Todo\Http\Middleware;
use Closure;
use Illuminate\Http\Response;
use Tymon\JWTAuth\JWTAuth;
//use Tymon\jwtAuthAuth\Exceptions\jwtAuthException;
//use Tymon\jwtAuthAuth\Exceptions\TokenExpiredException;
class SpirantJwtGetUserFromToken {
protected $jwtAuth;
public function __construct(JWTAuth $jwtAuth) {
$this->jwtAuth = $jwtAuth;
}
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, \Closure $next) {
if (!$token = $request->input('token')) {
//if (!$token = $this->jwtAuth->setRequest($request)->getToken()) {
$status = Response::HTTP_BAD_REQUEST;
return response()->json(['error' => 'Token Not provided', 'status' => $status], $status);
}
try {
$user = $this->jwtAuth->authenticate($token);
} catch (TokenExpiredException $e) {
$status = Response::HTTP_BAD_REQUEST;
return response()->json(['error' => 'Token Expired', 'status' => $status], $status);
//return $this->respond('tymon.jwtAuth.expired', 'token_expired', $e->getStatusCode(), [$e]);
} catch (jwtAuthException $e) {
$status = Response::HTTP_BAD_REQUEST;
return response()->json(['error' => 'Token Invalid', 'status' => $status], $status);
//return $this->respond('tymon.jwtAuth.invalid', 'token_invalid', $e->getStatusCode(), [$e]);
}
if (!$user) {
$status = Response::HTTP_NOT_FOUND;
return response()->json(['error' => 'Token Invalid', 'status' => $status], $status);
//return $this->respond('tymon.jwtAuth.user_not_found', 'user_not_found', 404);
}
//$this->events->fire('tymon.jwtAuth.valid', $user);
return $next($request);
}
}
Add the new route filter:
Route::group(array('prefix' => 'api/', 'middleware' => 'spirant.jwt.auth'), function () {
Route::resource('todo', 'TodoController');
//Route::resource('users', 'UserController');
});
Add to app > Http > Kernal.php into the $routeMiddleware: '
'spirant.jwt.auth' => 'Todo\Http\Middleware\SpirantJwtGetUserFromToken',
I will hopefully be looking at this again in a month or two so will update this with any improvements.
Newbie question... how did you add $rootScope to the dependencies for the module. Help me please?
angular.module('MainController', []).controller('MainController', ['$scope', '$location', '$localStorage', 'User',
function ($scope, $location, $localStorage, User) {
/**
* Responsible for highlighting the currently active menu item in the navbar.
*
* @param route
* @returns {boolean}
*/
$scope.isActive = function (route) {
return route === $location.path();
};
/**
* Query the authenticated user by the Authorization token from the header.
*
* @param user {object} If provided, it won't query from database, but take this one.
* @returns {null}
*/
$scope.getAuthenticatedUser = function (user) {
if (user) {
$scope.authenticatedUser = user;
return;
}
if (typeof $localStorage.token === 'undefined') {
return null;
}
new User().$getByToken(function (user) {
$scope.authenticatedUser = user;
}, function (err) {
console.log(err);
});
};
$scope.logout = function () {
delete $localStorage.token;
$scope.authenticatedUser = null;
$location.path('/auth/login');
};
$rootScope.$on('unauthorized', function() {
// Logged out on server so logout client side too (if not already)
$scope.logout();
// Load Login Form
$location.path('auth/login');
});
}
]);
Never mind... Thanks anyway...
angular.module('MainController', []).controller('MainController', ['$scope', '$rootScope','$location', '$localStorage', 'User',
function ($scope, $rootScope, $location, $localStorage, User) {
/**
* Responsible for highlighting the currently active menu item in the navbar.
*
* @param route
* @returns {boolean}
*/
$scope.isActive = function (route) {
return route === $location.path();
};
/**
* Query the authenticated user by the Authorization token from the header.
*
* @param user {object} If provided, it won't query from database, but take this one.
* @returns {null}
*/
$scope.getAuthenticatedUser = function (user) {
if (user) {
$scope.authenticatedUser = user;
return;
}
if (typeof $localStorage.token === 'undefined') {
return null;
}
new User().$getByToken(function (user) {
$scope.authenticatedUser = user;
}, function (err) {
console.log(err);
});
};
$scope.logout = function () {
delete $localStorage.token;
$scope.authenticatedUser = null;
$location.path('/auth/login');
};
$rootScope.$on('unauthorized', function() {
// Logged out on server so logout client side too (if not already)
$scope.logout();
// Load Login Form
$location.path('auth/login');
});
}
]);