Laravel oauth2 unauthorized redirect - laravel

I am trying to implement oath2 using laravel and passport. however i have managed to make everything up & running. But the problem i am facing is when token expires or without token the url redirects to the laravels login page. I was expecting an unauthorized error message with a status code 401.
oauth2 is implemented using passport. I think laravels default auth is creating the problem.
this is my code in routes
Route::group(['middleware' => 'auth:api'], function () {
Route::get('users', "\App\Http\Controllers\UserController#getUsers");
Route::post('permission/add', "\App\Http\Controllers\UserController#createPermission");
Route::get('permissions', "\App\Http\Controllers\UserController#getPermissions");
Route::get('permission/{param}', "\App\Http\Controllers\UserController#getPermission");
Route::post('role/add', "\App\Http\Controllers\UserController#createRole");
Route::get('roles', "\App\Http\Controllers\UserController#getRoles");
Route::get('user/profile', function () {
// Uses Auth Middleware
});
});
My output when unauthorized
my config/auth.php
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
],

Related

Laravel sanctum 401 Unauthorized

I'm trying to set-up Laravel authentication using sanctum. I'm able to create a successful login function, which returns a token (bearer), but once that bearer is added to a next request using postman, a 401 unauthorized is received, and I cannot figure out why. I'm running the setup using XAMPP (127.0.0.1:8000).
Config/auth contains the following guards
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users'
]
],
The config/sanctum guard is set to api.
I've added (and tested with) the following variables in the .env file;
SESSION_DOMAIN='localhost'
SANCTUM_STATEFUL_DOMAINS='localhost:8000'
Lastly, routes are set as follows;
Route::middleware('auth:sanctum')->group(function () {
Route::apiResource('friends', FriendsController::class);
});
I'm now unsure what the issue could possibly be.

Laravel api route post calls work as expected, get call return blank html response

My laravel app has multiple route handlers including web, api, admin, and a new one that's misbehaving: "advisor".
The web, api, and admin routes all work as expected.
For the advisor routes POST requests work as expected, but GET calls return the client-app.blade.php html file. Not authorized or 404, just that html file.
I have the advisor routes added in the map function just like the web and api routes are:
protected function mapAdvisorRoutes()
{
Route::prefix('advisor')
->middleware('advisor')
->namespace($this->namespace)
->group(base_path('routes/advisor.php'));
}
The auth guards are setup as follows (AWS Cognito authentication is working properly on POST):
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'advisor' => [
'driver' => 'cognito',
'provider' => 'users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'admins' => [
'driver' => 'eloquent',
'model' => App\Models\Admin::class,
]
],...
The issue is that the advisor GET calls are not executing the controllers or even middleware. advisor POST calls are authenticating and working as expected. Here are some example advisor routes:
Route::group(['prefix' => 'v1'], function () {
Route::group(['middleware' => 'auth:advisor'], function () {
Route::get('get-test', function () {
// ISSUE: is not returned
return 'advisors get test';
});
Route::post('post-test', function () {
return 'advisors post test - works as expected';
});
});});
My client-side app is sending the auth and accept headers - Authorization: Bearer {token} and Accept: "application/json"
Any idea what would cause the response to an api call to be the client-app.blade.php file?
Or why would laravel treat the POST vs GET calls differently?
Thanks!
As is often the case, writing out the problem helped me realize what was causing the problem.
The order of route mapping in RouteServiceProvider.php matters.
Changing this:
$this->mapApiRoutes();
$this->mapWebRoutes();
$this->mapAdvisorRoutes();
to this solved the problem:
$this->mapApiRoutes();
$this->mapAdvisorRoutes();
$this->mapWebRoutes();
The following line in web.php was causing the index or client-app.blade.php file to be returned.
Route::get('/{any}', 'ClientAppController#index')->where('any', '.*');
So my /advisor GET request was never getting to the advisor route logic.
Phew!

Problem with Laravel Sanctum SPA (multi auth) Authentication with Vue JS returning 'Not Authorized'

My Laravel project comprises of 3 separate users: admin, vendors and customers. The project is a mixture of server-side rendering with Vue components embedded here and there.
My guards is as follows
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'vendor-api' => [
'driver' => 'sanctum',
'provider' => 'vendors',
],
'admin-api' => [
'driver' => 'sanctum',
'provider' => 'admins',
],
'customer-api' => [
'driver' => 'sanctum',
'provider' => 'customers',
],
'admin' => [
'driver' => 'session',
'provider' => 'admins',
],
'vendor' => [
'driver' => 'session',
'provider' => 'vendors',
],
'customer' => [
'driver' => 'session',
'provider' => 'customers',
],
],
All my user models have the HasApiToken trait within them and I have done all the necessary setup as stipulated in the documentation.
When a vendor is logging in, this is the method that is run, first I do a get to the sanctum route then a post request to log in which returns the $vendor alongside it's token.
axios.get('/sanctum/csrf-cookie').then(response => { //first get call as stated in the documentations
console.log(response)
axios.post(this.route('api.vendors.login'), { //then a post call to a log in the user
email: this.email,
password: this.password
}).then(response => {
const token = response.data.token
axios.get(this.route('api.vendors.profile.show', 1))
.then(async (response) => {
console.log(response.data, "This is the vendor from the guarded api data");
this.$refs.loginForm.submit(); //this actually logs the user in using the web guard setup
})
.catch(function (error) {
console.log(error);
});
})
});
Now, I expect that the log in flow would have set the necessary token cookies in the browser (which it does as I can see it) and that a subsequent call to the API will be successful, it however returns as Unauthorized
However, when I used the token that was returned for example in Postman, I get a reply from the API and also in axios when the token is included in the header, the API works well. It's the Sanctum cookie/session based authorization that doesn't seem to be working.
My routes in the API are protected as follows as thus
Route::group(['middleware' => 'auth:vendor-api'], function () {
Route::resource('/profile', 'App\Http\Controllers\Api\Vendors\ProfileController', ['as' => 'api.vendors']);
});
I checked the headers for the unauthorized call and can see that the X_XRSF token is included.
My SESSION_DRIVER has been set to cookie.
My sanctum config has been set to local host
'stateful' => explode(',', env(
'SANCTUM_STATEFUL_DOMAINS',
'localhost,localhost:3002,localhost:3000,127.0.0.1,127.0.0.1:8008,::1,' . parse_url(env('APP_URL'), PHP_URL_HOST)
)),
'middleware' => [
'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
],
You need to add this to the js side.
axios.defaults.withCredentials = true;
as specified here https://laravel.com/docs/8.x/sanctum#cors-and-cookies

Restrict API users sign in from Web Admin Portal

My Laravel application has an API(students) and a web portal(admins). students register via API. Admin users are generated by a super admin. They login via the admin portal. App\User model has been used to facilitate both types of users with the Spatie Laravel Roles and Permissions.
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'token',
'provider' => 'users',
'hash' => false,
],
]
The problem is that the users who registered from the API can login via Web portal. But after login they cannot do anything because the web routes are having a role based middleware.
// web.php routes
Auth::routes(['register' => false]);
Route::group(['middleware' => ['role:admin']], function () {
Route::resource('classes', 'WEB\ClassController');
Route::resource('teachers', 'WEB\TeacherController');
});
// api.php routes
Route::post('register', 'API\RegisterController#register');
Route::post('login', 'API\LoginController#login');
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::resource('papers', 'API\PaperController');
Route::resource('marks', 'API\MarkController');
});
What can I do to handle this situation?
You could either display a message after login to tell students you don't have the permission to access the resource, or you could put the middleware on your web login route, so they can't even access the login page and see a 401 error right away

API JWT guard and web session guard in Laravel

I am trying to make Laravel using multiple guards - depending on the authentication type.
When I am authenticating via API, I would like to use jwt guard. When I am authenticating through web, I want to use session guard.
My config\auth.php looks like this:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
//'hash' => false,
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\User::class,
],
]
My routes/web.php looks like this:
Route::get('/', 'HomeController#index')->name('home');
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
My routes/api.php looks like this:
Route::post('login', 'AuthController#login');
Route::get('logout', 'AuthController#logout');
Route::post('refresh', 'AuthController#refresh');
Route::get('me', 'AuthController#me');
What I am getting is that API authentication works fine, while the web doesn't. I have debugged and it seems like it is always checking the jwt guard.
Can someone help me, how can I set the guard on a route if that is possible or on Auth::routes() call?
I have solved it by switching default to web routes.
Then I updated the code for app/Http/Controllers/AuthController.php to use the specific api auth wherever I was using the auth method:
auth('api')->attempt($validator->validated()) and 'expires_in' => auth('api')->factory()->getTTL() * 60

Resources