email/verify with VusJS SPA and Laravel - laravel

I'm currently building a new web application with VueJS SPA, VueJS Router, and Laravel, users should be able to access pages as guests (non-authenticated) or logged-in (authenticated)!
So $this->middleware('auth') is commented from my SpaContoller to give guests access to pages but with some view limitation of course!
I've added basic user authentication by using
php artisan make:auth
and the problem I'm facing is that after registration user gets redirected to the Home page and can access any pages rather than seeing the 'Verify Email' page only!
When I include $this->middleware('auth') for the SpaController it works fine but then guests can't access any pages.
So not sure now to get a proper solution for that?

I`m a beginner in Laravel and Vue-Js. I have done my website with JWT auth. I manage the access to pages using routes as follows.
routes: [
{ path: "/profile", component: profile, meta: { requireAuth: true } },
// this can be access only by registered users
{ path: "/home", component: home }, //this route can be access by anyone
router.beforeEach((to, from, next) => {
//console.log(Store.getters.role);
if (to.meta.requireAuth) {
next();
}
}
);

A Solution but I'm not sure if it would be the best one if you want to force the users to see only 'Verify Email' is to create a middleware and add it to SpaController:
class ForceRedirectToVerifyEmail extends Middleware
{
/**
*
* #param \Illuminate\Http\Request $request
* #return string
*/
protected function redirectTo($request)
{
if (auth()->check() && !auth()->user()->hasVerifiedEmail()) {
return url('verify-email');//Or what ever need to redirect them as normly it would be handled in VueJS or ReactJs themselves.
}
}
}

Related

Route type delete does not work in Laravel

I have following route and works
Route::post("delete-role", [RoleApiController::class, "Remove"]);
I tested it through postman like this
http://localhost/delete-role?api_token=hcvhjbhjb12khjbjhc876
Now, if I change above route and convert to type delete..
Route::delete("delete-role/{role_id}", [RoleApiController::class, "Remove"]);
it does not work. I get below error. It seems to be the reason that the api_token is missing.
I get same error when trying to update route like below
Route::delete("delete-role/{role_id}/{api_token}", [RoleApiController::class, "Remove"]);
You have to set header of your request as:
"Accept": "application/json"
in postman.
If you don't set the required header for api, Laravel Passport can't understand request as an API client and so it will redirect to a /login page for the web.
Or you can set a middleware to check it in code:
public function handle($request, Closure $next)
{
if(!in_array($request->headers->get('accept'), ['application/json', 'Application/Json']))
return response()->json(['message' => 'Unauthenticated.'], 401);
return $next($request);
}
You have an incomplete details. but I see few issues here.
You seem to be using web routes for your API requests which is a bad set-up
You do not have a route with login name.
based on the error you posted, your request seems to successfully destroyed the token and logged you out, then called the middleware App\Http\Middleware\Authenticate which supposed to redirect your request to login route which does not exist and the reason you are getting that error.
You can see from that Authenticate middleware it will supposed to redirect you to login route for unauthenticated request. thats why you need to use the api routes so you can handle the response manually
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* #param \Illuminate\Http\Request $request
* #return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}
Also, I'm not so sure about this, but the reason you are not getting the same issue with your POST request is probably because your POST request does not call the Authenticate middleware or whatever in your routes file or class function that calls the authenticate middleware.
But again, just use api routes if you don't want un-authenticated request to automatically redirect your request to login routes which does not exist in your application
The problem is that he doesn't define route ('login'),
add in Exceptions/Handler.php
$this->renderable(function (AuthenticationException $e, $request) {
if ($request->is('api/*')) {
return response()->json(['meassge' => 'Unauthenticated']);
}
});
Then you should use Passport Or Sanctum for auth with Api,
Continue from here https://laravel.com/docs/9.x/passport
Probably, this thread could help you. Route [login] not defined
(OR)
You need to setup auth scaffolding to get login route defined.
Important: your project will be overridden if you setup auth scaffold.
So, I would only recommend doing this if it is a new project or a testing app.
See this for detail doc but install Laravel Breeze would be suffice.
It Appears you have called route('login') without having defined it in your routes, Please remove route('login') from your code or define it in your routes. eg::
Route::get('login', [YourController::class, 'methodName'])->name('login');

Laravel maintenance mode on specific subdomain

I know that you can except some URIs of your main app like if you want to except example.com/page, you can just simply add it to the CheckForMaintenanceMode.php, like this:
In app/Http/Middleware/CheckForMaintenanceMode.php
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode as Middleware;
class CheckForMaintenanceMode extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* #var array
*/
protected $except = [
'/page'
];
}
Now, my app has a couple subdomains using one app; I have a subdomain for my main app: app.example.com, a subdomain for my API endpoints: api.example.com and the main website: www.example.com
How can I possibly except the specific subdomain instead of URI for maintenance mode? Like having api.example.com and app.example.com in maintenance mode but not the main website www.example.com?
I'm trying to figure out it on my own and even make my own middleware just to do this, but is it possible to do this using the built-in maintenance mode of laravel with php artisan:down?
Something like:
// app.example.com and api.example.com is in maintenance mode except:
protected $except = [
'example.com'
'www.example.com'
];
See the Illuminate\Foundation\Http\Middleware\CheckMaintenanceMode middleware class:
It checks the elements of the $except property using the function fullUrlIs() from the Illuminate\Http\Request class, which itself calls the Str::is() helper (also known as the str_is() function if you're using Laravel helper function globals):
protected function inExceptArray($request)
{
foreach ($this->except as $except) {
if ($except !== '/') {
$except = trim($except, '/');
}
if ($request->fullUrlIs($except) || $request->is($except)) {
return true;
}
}
return false;
}
See https://laravel.com/docs/7.x/helpers#method-str-is
You should then be able to check for an url like this, to exclude this domain from the maintenance mode (i.e. it will always be up):
protected $except = [
'https://www.example.com/*'
];
Suppose you have two domains. One is the main domain
and another one is a subdomain.
mydomain.com
admin.mydomain.com
You have a page name maintenance. The maintenance page is under the main domain. The URL of the maintenance page is mydomain.com/maintenance.
In the maintenance mode, you will have the route permission of mydomain.com/maintenance and admin.mydomain.com
Now work process.
Goto App\Http\Middleware the open the PreventRequestsDuringMaintenance middleware then add this code
protected $except = [
'maintenance*',
'http://admin.*',
'https://admin.*'
];
Then go to App\Exceptions open Handler file, inside render function add
if (App::isDownForMaintenance()) {
return redirect('/maintenance');
}
Now run php artisan down

Laravel nova - redirect from Dashboard

I would like to remove dashboard from my Laravel Nova app.
I found it easy to remove it from sidebar-menu - simply comment /views/dashboard/navigation.blade.php code.
However, I want to add a redirection logic (landing page depends on user role) so when navigating to / user will be redirected to a resource or tool which corresponds him.
(I have already implemented a redirection after login (https://stackoverflow.com/a/54345123/1039488)
I tried to do it with cards, but looks like this is not the right solution.
Any idea where can I place the redirection logic?
Nova 4; You can override the initialPath like so:
class NovaServiceProvider extends NovaApplicationServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Nova::initialPath('/resources/users');
}
// ...
}
This way, you get redirected to the Users resource upon logging in.
Pre nova 4 method:
To NovaServiceProvider.php add to boot method:
Nova::script('menuFix', __DIR__.'/../../resources/js/fixMenu.js');
Create file fixMenu.js with following:
if (location.pathname == '/' || location.pathname == '/dashboards/main'){
location.href = '/whereToRedirect'
}
A cleaner and safe way for Nova 3.x or below:
Copy vendor/laravel/nova/resources/views/layout.blade.php to resources/views/vendor/nova/
Now open resources/views/vendor/nova/layout.blade.php and edit it
Replace this line with the code below window.Nova = new CreateNova(config);
window.Nova = new CreateNova(config);
window.Nova.booting((Vue, router, store) => {
/** This fixes showing an empty dashboard. */
router.beforeEach((to, from, next) => {
if (to.name === 'dashboard.custom') {
next({ name: 'index', params: { resourceName: 'users'}});
}
next();
});
});
Replace users with your entity name's plural like products
Now save the file and refresh the nova dashboard and you should see the new page.
The solution was taken from here with clear steps.
The solution may also work for 4.x, but I haven't checked it yet.
Happy Coding :)
Just figured this out myself. In your Routes/web.php file, add a redirect route:
Route::redirect('/','/resources/{resource_name}');
where {resource_name} is the plural form of the resource. For example, '/resources/posts'.
In your case, you may want to redirect to your own control file, where the redirect logic can be placed.
Route::get('/', 'YourController#rootRedirectLogic');
Then in the controller YourController, add the method:
public function rootRedirectLogic(Request $request) {
// some logic here
return redirect()->route('YourRoute');
}
where 'YourRoute' is the name of the route you want to send the user to.
(Found clues to this solution in a comment by dillingham here: https://github.com/laravel/nova-issues/issues/393)
i came across this link : Laravel Nova - Point Nova path to resource page
Not sure it's a permanent solution but editing LoginController.php will do.
public function redirectPath()
{
return Nova::path().'/resources/***<resource_name>***;
}
**change to your own resource name

Laravel Passport API registering new users

I'm trying to build a login-system for my web-app, but I can't get Passport to work. The app is build as a REST API, so users should be able to register with an email and password, and after this they should be able to login with these credentials (so I think they will need to receive an access token from Passport when the login credentials are correct).
I thought I could just do a JSON post to a 'register' route to register a new user and then do a post to a 'login' route to get the access token back to the client, but there is no such thing as far as I can tell.
How do I register a new user?
If you're building SPA and using default Laravel register, login, forgot password, & reset password web functionality like Google Account for authentication & authorization purpose, you can override the registered method on App\Http\Controllers\Auth\RegisterController.php with redirect logic when intended url is exists.
This lines tells Laravel to look over intended url, prior navigation to the web register controller redirect path.
/**
* The user has been registered.
*
* #param \Illuminate\Http\Request $request
* #param mixed $user
* #return mixed
*/
protected function registered(Request $request, $user)
{
if ($request->session()->has('url.intended')) {
return redirect()->intended();
}
}
For example I'm using authorization code with PKCE grant on my Vue.js SFC
<template>
<v-app-bar app flat>
<button-login #login="authorize"></button-login>
<span>|</span>
<button-register #register="authorize"></button-register>
</v-app-bar>
</template>
<script>
import ButtonLogin from '#/components/Buttons/ButtonLogin'
import ButtonRegister from '#/components/Buttons/ButtonRegister'
import { base64URL, encrypt, hashMake, randomString } from '#/helpers'
import sha256 from 'crypto-js/sha256'
import httpBuildQuery from 'http-build-query'
import { SERVICE } from '#/config/services'
import { STORAGE_API_AUTHORIZATION_STATE, STORAGE_API_CODE_VERIFIER } from '#/config/storage'
export default {
name: 'AppBar',
components: {
ButtonLogin,
ButtonRegister
},
authorize() {
const authorizationState = randomString(40)
const codeVerifier = randomString(128)
const codeChallenge = base64URL(sha256(codeVerifier))
const query = httpBuildQuery({
client_id: SERVICE.CLIENT_ID,
redirect_uri: authorizationURL,
response_type: 'code',
scope: '*',
state: authorizationState,
code_challenge: codeChallenge,
code_challenge_method: 'S256'
})
localStorage.setItem(
STORAGE_API_AUTHORIZATION_STATE,
hashMake(authorizationState)
)
localStorage.setItem(
STORAGE_API_CODE_VERIFIER,
encrypt(codeVerifier, authorizationState)
)
location.href = `${SERVICE.API_URL}/oauth/authorize?${query}`
}
}
</script>
Whenever user click on login/register button on my SPA it'll redirect to my API OAuth authorization page.
The authenticate middleware will intercept the request and check for the logged in state of user, if user is not authenticated then it'll redirect user to the login page.
If user choose to register his/her account by clicking on the register button, we will redirect user to the web registration page (still on API not on SPA).
After the user is registered, the controller will call registered method and check for intended URL existence, if exists then we are able to redirect user to the intended url (the oauth/authorize endpoint), and the authorization process can be continued after registration process.
I'm facing the same problem here, and by now the best solution I've found is to create the register method manually by creating a UserController and a store method like this
public function store(Request $request) {
$data=$request->only('name', 'email','password');
$valid = validator(
$data, [
'name' => 'required|string|max:255',',
'email' => 'required|string|email|max:155|unique:users',
'password' => 'required|string|min:4',
]);
$return=null;
if ($valid->fails()) {
$return = response()->json($valid->errors()->all(), 400);
}else{
$data['password']=Hash::make($data['password']);
#return = User::create($data);
}
return $return;
}

php laravel preventing multiple logins of a user from different devices/browser tabs at a given time

Does laravel provide a way to prevent multiple logins of a user from different devices / browsers at a given time? If yes then how can i force a user to logged in from a single device at a single time. I am developing a online quiz app using laravel 5.6 where users can logged in from a single place and take test.
laravel provide this method to invalidating and "logging out" a user's sessions that are active on other devices logoutOtherDevices()
to work with this method you need also to make sure that the
Illuminate\Session\Middleware\AuthenticateSession
middleware is present and un-commented in your app/Http/Kernel.php class' web middleware group:
'web' => [
// ...
\Illuminate\Session\Middleware\AuthenticateSession::class,
// ...
],
then you can use it like this
use Illuminate\Support\Facades\Auth;
Auth::logoutOtherDevices($password);
Perhaps this should get you started:
Add a column in users_table.php
$table->boolean('is_logged_in')->default(false);
When a user logs in: LoginController.php
public function postLogin()
{
// your validation
// authentication check
// if authenticated, update the is_logged_in attribute to true in users table
auth()->user()->update(['is_logged_in' => true]);
// redirect...
}
Now, whenever a user tries to login from another browser or device, it should check if that user is already logged in. So, again in LoginController.php
public function index()
{
if (auth()->check() && auth()->user()->is_logged_in == true) {
// your error logic or redirect
}
return view('path.to.login');
}
When a user logs out: LogoutController.php
public function logout()
{
auth()->user()->update(['is_logged_in' => false]);
auth()->logout();
// redirect to login page...
}

Resources