I'm currently using Laravel Passport, and I can verify that there is current token saved by using localhost:8000/api/check that returns json below:
{"id":"1c080ff73c6592b8e35630ae36f45f5042c04d9a9ed26a7fafc3793c606484b619ed8792be65a658","user_id":1,"client_id":5,"name":"Personal Access Tokens","scopes":["administrator"],...}
But when I tried to use middleware scope for admin using localhost:8000/api/admin it returns an error
Illuminate\Contracts\Container\BindingResolutionException: Target class [scope] does not exist. in file
Here is the routes/api.php
Route::group(['middleware' => 'auth:api'], function(){
Route::get('check', 'TeamController#check');
Route::group(['middleware' => 'scope:administrator'], function() {
Route::get('admin', 'TeamController#index');
});
});
Here's the corresponding functions on TeamController.php
public function check(Request $request) {
return auth()->user()->token();
}
public function index(Request $request) {
return auth()->user()->token();
}
Somebody knows what I went wrong?
You most likely did not register the scope middleware.
"Passport includes two middleware that may be used to verify that an incoming request is authenticated with a token that has been granted a given scope. To get started, add the following middleware to the $routeMiddleware property of your app/Http/Kernel.php file:"
'scopes' => \Laravel\Passport\Http\Middleware\CheckScopes::class,
'scope' => \Laravel\Passport\Http\Middleware\CheckForAnyScope::class,
Laravel 7.x Docs - Passport - Checking Scopes
Related
Hello i created a basic laravel application (apis only) and added jwt authentication to it using 2-3 different tutorials online..
After completing all steps, we have the following routes:
Route::group([
'middleware' => 'api',
'prefix' => 'auth'
], function ($router) {
Route::post('/login', [AuthController::class, 'login']);
Route::post('/register', [AuthController::class, 'register']);
Route::post('/logout', [AuthController::class, 'logout']);
Route::post('/refresh', [AuthController::class, 'refresh']);
Route::get('/me', [AuthController::class, 'userProfile']);
});
The AuthController Class has the following constructor which is enforcing the middleware to the controller except for login and register routes which don't require a token:
class AuthController extends Controller
{
public function __construct() {
$this->middleware('auth:api', ['except' => ['login', 'register']]);
}
This is how the authentication defaults and guards are configured in the config/auth.php file:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
'hash' => false,
],
],
Now when i call the /auth/register route with appropriate data, the route returns 201 and a user is created successfully.
Then i call the /auth/login route using the credentials specified during the registration step, which also succeeds and returns a valid jwt token.
Now, when i use the generated token, and i use the /auth/refresh, /auth/logout, or /auth/me routes, they all function as expected.
however, if logout is called (which makes the token get invalidated) of if no bearer token is provided as authorization header, then the server returns a 500 Internal Error with the following Message.
Symfony\Component\Routing\Exception\RouteNotFoundException: Route
[login] not defined. in file
/path/to/project/vendor/laravel/framework/src/Illuminate/Routing/UrlGenerator.php
on line 444
It seems after logging out, or if no token is specified or token is not valid, then the middleware is calling the following redirectTo($request) function found i the authenticate middleware
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
However, I don't have a named login route; So, i went to my routes/api.php file and modified the /login route and named it login using the following syntax.
Route::post('/login', [AuthController::class, 'login'])->name('login');
and now I'm getting a new 405 error, method not allowed.
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException:
The GET method is not supported for this route. Supported methods:
POST. in file
/path/to/project/vendor/laravel/framework/src/Illuminate/Routing/AbstractRouteCollection.php
on line 117
As if the invalid token is forcing the middleware to redirect to the login route but as a GET method which does not exist.
What am i missing?
p.s. if i put the logout route inside the AuthControllers' constructer's except list, i get no error which makes sense. But I don't want to do that, i just want to return a json object saying unauthorized.
What I have tried, without being convinced that it's the right way of solving the problem, is the following:
First, I Created a new GET /unauthorized route and named it: unauthorized
Route::get('/unauthorized', [AuthController::class, 'unauthorized'])->name('unauthorized');
Then, I modified the redirectTo function in the Authenticate class so that it redirects to the /unauthorized route
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('unauthorized');
}
}
But I'm not convinced it's the right way of solving the problem, because I don't know when does the redirectTo function gets called (I mean all the use cases) and i could have stopped something else.
Any hints on why this is happening? and what have I done wrong is appreciated!
I'm trying to use the laravel passport to authenticate users. I followed every step in laravel passport documentation. But it shows error when I access a route that is protected by 'auth:api' middleware. I've searched over the internet but any of the answers are not working so far. Please see the code, and what I've tried so far below.
Tech used
Laravel 6
Laravel Passport
Vue 2
Axios
Tried so far:
Changed Passport::routes(); to Passport::routes(null, ['prefix' => 'api/oauth', 'middleware' => ['auth:api', 'web', 'auth']]); in AuthServiceProvider. Also tried Passport::routes(null, ['prefix' => 'api/oauth');
Tried to run php artisan cache:clear
routes/api.php
Route::middleware('auth:api')->group(function() {
Route::get('user', function() {
return request()->user();
});
Route::get('posts', 'PostController#index')->name('posts');
Route::post('posts/store', 'PostController#store')->name('posts.store');
Route::get('posts/{id}/show')->name('posts.show');
Route::get('users/{id}/show')->name('users.show');
});
Axios
mounted() {
axios.get('/api/posts')
.then(res => this.posts = res.data).catch(error => console.log('Unable to fetch posts.'))
}
Error
Headers:
Please let me know if you need some details that is not indicated on my post yet.
Setup
Run composer require laravel/passport:9 for the installation.
Run php artisan migrate for the tables.
Run php artisan passport:install to generate the encryption keys.
Added Laravel\Passport\HasApiTokens trait in User model.
Added Passport::routes(); in AuthServiceProvider.
Changed guard's api driver to passport in config/auth.php.
Full source code:
Click this link
I think you should review your code with the following steps.
Step 1: User model has to use HasApiTokens
...
class User extends Authenticatable
{
use HasApiTokens, Notifiable;
}
Step 2: Passport::routes method within the boot method of your AuthServiceProvider
class AuthServiceProvider extends ServiceProvider
{
...
public function boot()
{
$this->registerPolicies();
Passport::routes();
}
}
Step 3: In your config/auth.php configuration file, you should set the driver option of the api authentication guard to passport
'guards' => [
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],
Step 4 (Optional):
If you want to consume your API from your JavaScript application, you need to manually send an access token to the application and pass it with each request to your application. However, Passport includes a middleware that can handle this for you. All you need to do is add the CreateFreshApiToken middleware to your web middleware group in your app/Http/Kernel.php file:
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
! You should ensure that the CreateFreshApiToken middleware is the last middleware listed in your middleware stack.
Edit (2021/10/17):
You should try to add the auth middleware to $middlewareGroups of your Kernel.php
protected $middlewareGroups = [
...
'api' => [
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
'auth:api', //Need to add
],
];
Edit (2021/10/25):
I downloaded your code and debug it. When you debug it, you will get the following result:
Header JWT is not valid for JSON structure. There seems to be an incompatibility between the Laravel Passport library and your system. You should check your system version (PHP, Laravel, etc). I will continue to investigate. If you have any new information, please let me know
Edit (2021/11/02):
Update your laravel/framework dependency to ^6.0 in your composer.json file. If installed, update your laravel/passport dependency to ^9.3.2 in your composer.json file.
https://laravel.com/docs/6.x/upgrade#updating-dependencies
According to Laravel 6.x documentation, the version of Laravel Passport is from 9.3.2. Please try this version.
If you are using Api/oauth prefix AND add the auth:api middleware than you need to authenticate via Bearer tokens.
So it may be you have csrf token related issue so try below to disable it.
Passport::ignoreCsrfToken(true);
You should add token bearer into your ajax requests. You can get it after successful login.
// laravel
use Illuminate\Support\Facades\Auth;
use App\Models\User;
public function login()
{
$credentials = request(['email', 'password']);
$user = User::where('email', $credentials['email'])->first();
if (!$user || !$token = Auth::attempt($credentials)) {
return response()->json(['success'=> false, 'error' => 'неверный логин или пароль', 'cr' => $credentials], 422);
}
return $this->respondWithToken($token);
}
// vue login
async function login(email, password) {
try {
const response = await axios.post('/auth/login', {email, password});
const token = response.data.token;
// some func to store token, I am using vuex mutations for this
storeToken(token);
}
catch(error) {
console.error(error);
}
};
// vue regular function
async function changePassword(newPassword) {
// get token from global storage, with vuex it will be like store.state.auth.token
const token = getToken();
await axios.post('/user/changePassword', {newPassword}, headers: { Authorization: `Bearer ${token}`});
}
You've forgotten to run php artisan passport:keys ...so there may be no RSA key-pair available, which is crucial. Clients usually can be added with: php artisan passport:client. And generally speaking, there is no default recipe for debugging Laravel middleware ...there only is xdebug.
I have the following lines in my routes/api.php
Route::middleware('api')->get('/posts', function (Request $request) {
Route::resource('posts','ApiControllers\PostsApiController');
});
When I hit http://localhost:8000/api/posts it comes back blank, but when I move the above route to routes/web.php like so:
Route::group(['prefix' => 'api/v1'],function(){
Route::resource('posts','ApiControllers\PostsApiController');
});
it works.
As a reminder I have cleared the routes cache file with php artisan route:clear and my route list comes with php artisan route:list when my routes/web.php is empty and routes/api.php has the above route:
Domain
Method
URI
Name
Action
Middleware
GET|HEAD
api/posts
Closure
api
Note that with web routes part the list comes ok and works fine.
What am I doing wrong here?
Dont use the middleware api and see following route example for API routes
Example 1 (in your api.php)
Route::get('test',function(){
return response([1,2,3,4],200);
});
visit this route as
localhost/api/test
Example 2 (if you want api authentication, token based auth using laravel passport)
Route::get('user', function (Request $request) {
///// controller
})->middleware('auth:api');
You can make get request for this route but you need to pass the access token because auth:api middleware has been used.
Note: see /app/http/kernel.php
and you can find the
protected $routeMiddleware = [
//available route middlewares
]
There must not be such (api) kind of middle ware in this file (kernel.php) for routes unless you create one, that why you can not use middleware as api.
Here, How I am creating REST APIs (api.php)
//All routes goes outside of this route group which does not require authentication
Route::get('test',function(){
return response([1,2,3,4],200);
});
//following Which require authentication ................
Route::group(['prefix' => 'v1', 'middleware' => 'auth:api'], function(){
Route::get('user-list',"Api\ApiController#getUserList");
Route::post('send-fax', [
'uses'=>'api\ApiController#sendFax',
'as'=>'send-fax'
]);
Route::post('user/change-password', [
'uses'=>'api\ApiController#changePassword',
'as'=>'user/change-password'
]);
});
When user enter username and password on the the browser and successfully logged in.
I like to make some API requests after user have logged in.
Laravel 5.3 provide api.php in routes folder.
in api.php I have included:
Route::group(['middleware' => ['auth']], function () {
Route::get('/test', function (Request $request) {
return response()->json(['name' => 'test']);
});
});
When requesting domain.com/api/test on the browser, for some reason it is redirecting to /home?
API token is not needed.
If you are specifying routes in api.php, you will need to use the auth:api middleware. So using your example it would be:
Route::group(['middleware' => ['auth:api']], function () {
Route::get('/test', function (Request $request) {
return response()->json(['name' => 'test']);
});
});
Notes about Token auth and Laravel 5.3:
If you've setup laravel's default auth system, you will also need to add a column for api_token to the user table. If you are using DB seeders, you might want to add something like:
$table->char('api_token', 60)->nullable();
to your users table seeder. Alternatively just add the column manually and fill that column with a random 60-char key.
When making the request, you can add the api_token as a URL/Querystring parameter like so:
domain.com/api/test?api_token=[your 60 char key].
You can also send the key as a header (if using Postman or similar), i.e:
Header: Authorization, Value: Bearer [your 60 char key].
I order to get a useful error if the token is incorrect, and not just be redirected to login, also send the following header with all requests:
Header: Accept, Value: application/json. This allows the expectsJson() check in the unauthenticated() function inside App/Exceptions/Handler.php to work correctly.
I found it hard to find clear docs from Laravel about using token auth with 5.3, I think it's because there's a drive to make use of Passport, and it supports tokens in a different way. Here's the article that probably helped most getting it working: https://gistlog.co/JacobBennett/090369fbab0b31130b51
first install the passport as stated here laravel passport installation
while consuming your own api add below line in your config/app.php in middleware section
'web' => [
// Other middleware...
\Laravel\Passport\Http\Middleware\CreateFreshApiToken::class,
],
now change your route to
Route::group(['middleware' => ['auth:api']], function () {
Route::get('/test', function (Request $request) {
return response()->json(['name' => 'test']);
});
});
now in your config/auth.php change these lines
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
The reason you are being redirected back to home is because the auth middleware checks if a user session is stored in your browser, but since api middleware does not make use of sessions (see app\http\kernel.php), your request is considered unauthenticated
If you would like to perform simple APIs that utilize sessions, feel free to add them in your web routes, and make sure to secure them by grouping them inside an auth middleware.
The standard behaviour in Laravel 5.5 is to delegate handling of authentication exceptions to app/Handler::unauthenticated(), in your project's application code. You'll find the code in there that redirects to the login page, and you can override it or perform further tests and contextualization in there. In previous versions of Laravel, 5.3 among them I believe, this exception handling was executed way down within the Laravel library within the vendor folder.
I have a bunch of controllers. One of them is ArticleController. I want the method postCreateArticle() method to require the user to be authenticated.
In the documentation, I figured you can use the auth middleware, like so:
Route::get('profile', ['middleware' => 'auth', function()
{
// Only authenticated users may enter...
}]);
However, I am registering my controllers in the routes:
Route::controller('articles', 'ArticleController');
How do I protect the postCreateArticle() method, without doing it inside the method?
In your constructor you should be able to:
$this->middleware('auth', ['only' => 'postCreateArticle'])