Laravel 7 UI - Sessions Guard Driver - Spatie Permission Package - laravel

In short: The Sessions guard driver is rejecting my login attempts.
Before creating the Laravel UI I had already created a authentication system for my frontend using JWT as the api guard driver.
I installed the Laravel UI in order to use the Spatie Permission Package which had also been installed and had generated some extra tables.
Once the Laravel UI was installed I successfully registered a new user but was not redirected to the home screen. I attempted to log in as the new user and got a response stating that my credentials were not recognised. I then tried to log in with a user that had been previously created in the users table through the frontend registration...
Whilst it gave no error this time it still did not redirect me. I then attempted to log into the frontend app with the newly created user and was able to do so. At this point I figured there was a conflict with the tokens.
I looked in RegisterController.php and saw the following:
protected function create(array $data)
{
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
]);
}
My frontend JWT auth system also uses User.php and so I had a look and saw that the password was being encrypted twice. First with Hash:: and then with bcrypt. I therefore removed the Hash encryption.
I registered successfully but again was not redirected to home. I attempted to log in again and was again unable to do so, but this time I received no error message! The registered user was now behaving in the same manner as users registered through the front end app.
I went through the code line-by-line spending hours logging out values etc. and could see that everything was being accepted and I was being redirected to home but the HomeController.php was then passing my credentials through the auth guard which was rejecting then and then redirecting me back to login!!
The only way I can access home is by changing the middleware in HomeController.php from 'auth' to 'guest':
public function __construct()
{
$this->middleware('guest'); // changed from 'auth'
}
In the Spatie documention it states the following:
If your app uses only a single guard, but is not web (Laravel’s default, which shows “first” in the auth config file) then change the order of your listed guards in your config/auth.php to list your primary guard as the default and as the first in the list of defined guards. While you’re editing that file, best to remove any guards you don’t use, too.
I therefore switched the guards around in config/auth.php so that api was first but it had no effect.
'guards' => [
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
'web' => [
'driver' => 'session',
'provider' => 'users',
],
],
The frontend is using the api guard and the backend is using web but they are both using the same app/User. This I guess must be the cause of the problem but I am new to Laravel and, after hours of reading and testing, I am still struggling. What does it want that I am missing!? The password is now being accepted so it cannot be that..
Any help would be very much appreciated. Thank you.

The problem was in the top section of config/auth which I had missed:
'defaults' => [
'guard' =>'api',
'passwords' => 'users',
],
It wasn't that I was being rejected by the web session driver - it was that the web guard was being overwritten by the api guard in the defaults section above.
I have to specify in the routing which middleware I wanted so as to avoid the default:
Route::get('/home', [
'middleware' => 'auth:web', 'uses' => 'HomeController#index'])->name('home');
EDIT: Since auth:web was only used by a couple of controllers the better solution was to change the default guard in config/auth to web and then do Route::group with the rest that used the api guard:
Route::group([
'middleware' => 'api'
], function () {
Route::post('login', 'AuthController#login');
Route::post('register', 'AuthController#register');
Route::post('logout', 'AuthController#logout');
Route::post('refresh', 'AuthController#refresh');
});

Related

How to use Laravel and Nuxtjs with Authentication, including login, logout, and password resets?

I can't find any resource to help with this issue, there are some repos that provide some type of base, but not many of them actually work.
Goals: Run Laravel has the backend API, run NuxtJS as the frontend SPA, either 2 separate locations or combined into one.
Needs to have proper authentication between both systems for logging in users. Laravel Sanctum looks to solve some of the SPA issues, but its hard to find proper documentation that actually shows a fully setup example.
Tested this idea https://github.com/zondycz/laravel-nuxt-sanctum But has failed, doesnt work with npm, must use yarn, however, login errors, doesnt work out of box. Repo needs work.
This tutorial Secure authentication in Nuxt SPA with Laravel as back-end was very indepth, and looked promising, however, refresh tokens don't seem to work with the SPA side since the author developed it on static nuxt. Though I feel like it could be modified to work, I havent found the solution yet.
This template, Laranuxt looked very promising, though I've yet to try it, im not sure if they are regularly updating it at this point, which was previously built by Laravel Nuxt JS (abandoned project)
I was able to run the #2, while refresh tokens dont exactly work, I can still authenticate the user, but now the other issue is password resets, which I'm unable to properly setup through the nuxt form.
Has anyone found resources or solved this issue with communication between these frameworks? or am I going done a rabbit hole that seems to have no end in sight?
I guess another way could be saying, can you do a fully restful authentication system?
Hopefully this isn't too broad of a topic, looking for some guidance on this issue as its hard to find proper tutorials or documentation without writing too much core code myself.
It seems many people struggle to implement Sanctum for SPA authentication when splitting the front and back across separate domains, and the problem is usually CORS related. The Sanctum documentation is great, but assumes a knowledge of CORS (or assumes requests will be same-origin). I'll break down the setup as I see it, providing a little extra support where I feel the docs fall short. A long answer, but towards the end I will address your question which seems to focus specifically on authentication.
Taken from Sanctum documentation:
First, you should configure which domains your SPA will be making requests from.
Assuming your front-end app lives at https://www.my-awesome-app.io, what is the domain? What about http://localhost:3000? Domains map to IP addresses, not protocols or port numbers. So the domains in the given examples would be www.my-awesome-app.io and localhost. With that in mind, all you need to do at this stage is go to the sanctum.php file in your config directory and set the value of the 'stateful' key to match the domain your Laravel API will receive requests from. Although domain names by definition do not include port numbers, the Sanctum docs make it very clear this is also required if you're accessing via a URL that requires a specific port.
/config/sanctum.php
...
'stateful' => [
'localhost:3000',
],
or
'stateful' => [
'my-awesome-app.io',
],
.env files are useful here.
If you are having trouble authenticating with your application from an SPA that executes on a separate subdomain, you have likely misconfigured your CORS (Cross-Origin Resource Sharing) or session cookie settings.
Indeed. So what does a correct setup look like? Assuming a recent Laravel version using the fruitcake/laravel-cors package, you will have a cors.php file in your /config folder. The default looks like:
Default
/config/cors.php
...
'paths' => ['api/*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
We have some work to do here. First, the paths. At the moment, our Laravel API is set up to allow requests from any external origin only if they are trying to access the /api/ routes*. This can lead to trouble early on when, as the Sanctum docs require, we try to access a csrf cookie from the path /sanctum/csrf-cookie. Requests to this path are not explicitly permitted in our cors.php file, so they will fail. To fix, we could do this:
'paths' => [
'api/*',
'sanctum/csrf-cookie'
]
and now requests to /sanctum/csrf-cookie will be permitted. As a sidenote, I find it personally very useful to change the prefix from sanctum to api, that way I can set a single base url for my http client (usually axios).
import axios from 'axios';
axios.defaults.withCredentials = true;
axios.defaults.baseURL = 'http://localhost:3000/api';
To change the path, you can change the following in the /config/sanctum.php file:
'prefix' => 'api',
Now GET requests to /api/csrf-cookie will return the cookie, instead of /sanctum/csrf-cookie.
Next, the allowed-origins. By default, it is set to *, which means "any origin". An origin is the protocol, domain and port number of the app sending a request to your Laravel API. So going back to our earlier examples, their origins would be http://localhost:3000 and https://www.my-awesome-app.io. These are the exact values you should use to allow requests from your front-end app:
'allowed_origins' => ['http://localhost:3000'],
I would recommend moving this to the .env file, and having a separate origin for local and production.
/config/cors.php
...
'allowed_origins' => [env('ALLOWED_ORIGINS')],
/.env
...
ALLOWED_ORIGINS=http://localhost:3000
The documentation does mention the last part of our cors config, which is that
'supports_credentials' => false,
Must be changed to:
'supports_credentials' => true,
Our /config/cors.php file now looks like:
Modified
/config/cors.php
...
'paths' => [
'api/*',
'sanctum/csrf-cookie'
],
'allowed_methods' => ['*'],
'allowed_origins' => [env('ALLOWED_ORIGINS')],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => true,
Bonus info, Chrome will not allow a credentialed request to a server that returns the header
Access-Control-Allow-Origin: *
Google Chrome: A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true
So you should make sure you set a specific origin in your cors config!
Finally, you should ensure your application's session cookie domain configuration supports any subdomain of your root domain. You may do this by prefixing the domain with a leading . within your session configuration file:
This isn't complicated, but seems it can catch people out so thought I'd mention it. Given our examples so far, we'd make the following change to our config/session.php file:
'domain' => '.my-awesome-app.io',
Locally, localhost alone is fine:
'domain' => 'localhost',
Assuming you've followed the rest of the instructions in the Sanctum documentation (setting axios.defaults.withCredentials = true;, adding the middleware etc) your backend configuration is now complete.
Front end and authentication.
I love Sanctum and I'm very grateful for the creators; so I say this with respect; the documentation lacks a little depth at this point. Grabbing the csrf-token is very straight forward, and then...
Once CSRF protection has been initialized, you should make a POST request to the typical Laravel /login route. This /login route may be provided by the laravel/jetstream authentication scaffolding package.
If the login request is successful, you will be authenticated and subsequent requests to your API routes will automatically be authenticated via the session cookie that the Laravel backend issued to your client.
It seems they've updated the docs!
As I write this, I've checked the latest docs and it's now highlighted the fact that you are free to write your own login endpoint. This was always the case, but might have escaped a few people, perhaps given the instructions above ("you should make a POST request to the typical Laravel /login route.") It's also perhaps not clear that you can override default Laravel methods to prevent unwanted side-effects of the default auth setup, like redirecting to the /home page etc.
Writing your own login controller is simple, and I prefer to do so for Sanctum. Here's one you can use:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;
class LoginController extends Controller
{
public function login(Request $request)
{
$request->validate([
'email' => ['required', 'email'],
'password' => 'required'
]);
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
return response()->json(Auth::user(), 200);
}
throw ValidationException::withMessages([
'email' => 'The provided credentails are incorect.'
]);
}
}
Feel free to modify this to suit your needs.
How you manage your state (making sure your app remembers you are logged in, for example) on the front-end is also up to you. There are lots of options, however if we're using Sanctum I think we should focus on a simple cookie-based approach. Here's one I use:
Login to your app. An auth session is established and your browser saves the cookies provided by your Laravel API.
Have your login script return the authenticated user (the one provided above does just that). Save the details of that user to your app state (eg. Vuex).
Check your state contains the user any time you need to secure an action against unauthorised users. Redirect to the login page is the auth check fails.
Here's the above in Nuxt.js form using middleware.
/middleware/auth-check.js
export default async function ({ store, redirect }) {
// Check if the user is not already in the store.
if (store.state.user === null) {
// Call your Laravel API to get the currently authenticated user.
// It doesn't matter if the store has been wiped out due to a page
// refresh- the browser still has the cookies, which will be sent
// along with this request.
try {
let rsp = await user.getAuthenticatedUser()
// If we get the user from the Laravel API, push it back in to
// the store and carry on to the page.
store.commit('SET_AUTH_USER', rsp.data)
} catch (e) {
// If our API doesn't return the user for any reason, redirect to
// the login page.
return redirect('/login')
}
}
// If not, carry on to the page.
}
/pages/admin.vue
export default {
middleware: auth-check
}
The code above is for example purposes, but it's generally what I use for Vue/Nuxt and Sanctum.
Hope this helps, happy to elaborate further if anyone can benefit.

Can one user table be used for both API and regular authentication?

I have multiple front end apps using one database. I wanted to use a single user table to authenticate users for all apps. The problem is some apps access tables in the database through regular authentication while some are isolated reacts apps accessing the database through API authentication.
My question is, can I use the same user table to authenticate users from different apps using both regular and API authentication. For your info, I am using Laravel as a backend. Thank you for your help :).
I guess with regular authentication you mean session and the answer is yes.
Because authentication data and logic are always separated from the authenticatable table (when using jwt you get another table for tokens and with session authentication you use cache or database).
so yes you will use as many guards as you wish with the same table it will look like this in your config/auth.php file:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'jwt',
'provider' => 'users',
],
],

Socialite Google redirect_uri_mismatch

I am trying to login with google using socialite , my facebook login works fine so the problem in my google app, any help!!!
'google' => [
'client_id' => 'app-key',
'client_secret' => 'app-secret',
'redirect' => 'http://www.shoptizer.com/callback/google',
],
Also one more point to remember that Socialite also gives uri_mismatch_error even when your redirects are correctly defined in google console but you dynamically changed the redirectUrl through
return Socialite::with('google')->redirectUrl($redirect_url)->redirect();
So plz take care that you should also need to define while receiving the response
Socialite::driver('google')->redirectUrl($redirect_url)->stateless()->user();
where $redirect_url is your custom redirect url.
After google redirects you to correct place, but even then Socialite checks it at its end.
I found this link https://blog.damirmiladinov.com/laravel/laravel-5.2-socialite-google-login.html
From this tutorial:
Occasionally it happens that google require some time to apply client configuration If you get an error message redirect_uri_missmatch wait couple of minutes and it should work normally.
Also change the redirect uri by:
'google' => [
'client_id' => 'app-key',
'client_secret' => 'app-secret',
'redirect' => 'https://www.shoptizer.com/callback/google',
],
If your app is provided by https you must match your http scheme on google api and on your redirect callback.
The problem is in the default url, you must change it on two occasions: before the redirect and before getting the user data.
Do not do this:
return Socialite::driver('google')->redirectUrl($yourredirecturl)->redirect();
Do it:
config()->set('services.google.redirect', $yourredirecturl);
return Socialite::driver('google')->redirect();
And when accessing user data, do this:
config()->set('services.google.redirect', $yourredirecturl);
$user = Socialite::driver('google')->user();

Can I use OAuth and Auth at the same time in Laravel?

I am doing a project in which I have implemented private chat in Laravel. But for the third party, we use OAuth but i have already used auth() in my project. Can I use both? OAuth is getting token, then communicate with Vue.js. So, I don't want to remove auth() functions in my project. Can you please guide me what to do?
Real time chat system in laravel project. I'm using separate Vue.js with Laravel.
Yes. You can use both OAuth and default Laravel Auth at the same time. In default, Laravel provides routes as web.php and api.php.
web.php: This route uses default Laravel Auth functionality
api.php: Routes defined here uses OAuth functionality
Make sure you use default driver as web in config/auth.php
'defaults' => [
'guard' => 'web',
'passwords' => 'users',
],

Registration error in Laravel

I use the default auth/registration in Laravel 5.3.
When I try to register new user I get error:
FatalThrowableError in RegistersUsers.php line 33:
Call to undefined method Illuminate\Auth\TokenGuard::login()
I have made some changes in configuration Laravel:
'defaults' => [
'guard' => 'api',
'passwords' => 'users',
],
So, by default I use api guard
The authentication driver api you are trying to use is TokenBased. That means the server will issue your client a Token on successful authentication using credentials. Then, client can present this token to server while making the request to identify itself.
As given in laravel git, there isn't any method as login().
To use the TokenBased Authentication, here's the good guide

Resources