Using cookies in laravel to get referral code id? - laravel

I have generated a referral code for each user on my platform, and would to use cookies to track if a new user has signed up through someone else(aka. they got referred).
I can generate a url fine, i.e., http://localhost:8888/Test/public/?ref=111222333444
But the cookie doesn't appear to be storing and translating back to my database when I use the code to sign up as a new user
What am I missing?
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Response;
use Closure;
class CheckReferral
{
public function handle($request, Closure $next)
{
if( $request->hasCookie('referral')) {
return $next($request);
}
else {
if( $request->query('ref') ) {
print "yes cookie detected";
return redirect($request->fullUrl())->withCookie(cookie()->forever('referral', $request->query('ref')));
}
}
return $next($request);
}
}

Okay this was my dumb mistake - I overlooked adding the Middleware to my route. Solved it simply by doing this:
use App\Http\Middleware\CheckReferral;
Route::get('/', function () {
return view('welcome');
})->middleware(CheckReferral::class);

Best way to use middleware is to add it in Kernal class by setting it against routeMiddleware array e.g
$routeMiddleware = [
'auth' => \Laravel\Http\Middleware\Authenticate::class,
'guest' => \Laravel\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
...
'check-referral' => \Laravel\Http\Middleware\CheckReferral::class,
];
and then assign the route e.g
Route::get('/', function () {
//
})->middleware('check-referral');
Hope this will help

Related

Login and verify user - Call to a member function getKey() on null

I am trying to create a custom verification flow, where as soon as a user clicks the verification link, it logs him in and also verifies him, instead of first making him log in and only then the verification link works.
I built a custom notification URL in my CustomVerificationNotification, including the registered user_id, to login him later:
protected function verificationUrl($notifiable)
{
if (static::$createUrlCallback) {
return call_user_func(static::$createUrlCallback, $notifiable);
}
return URL::temporarySignedRoute(
'verification.custom-verify',
Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
[
'id' => $notifiable->getKey(),
'hash' => sha1($notifiable->getEmailForVerification()),
'user_id' => $this->user->id
]
);
}
Then in my web.php I added this route:
Route::get('/email/verify/{id}/{hash}/{user_id}','Auth\CustomVerifyController#login_and_verify')->name('verification.custom-verify');
Then in my CustomVerifyController:
public function login_and_verify(EmailVerificationRequest $request)
{
//..
}
But I get Call to a member function getKey() on null. And I can't edit EmailVerificationRequest, so what can I do? Is it possible to somehow call Auth::login($user); before calling the EmailVerificationRequest? (Because I have the user_id from the route)
I tried to follow the best answer from this post as well: How to Verify Email Without Asking the User to Login to Laravel
But I'm not sure then how to trigger the verify() method from the web.php and send the $request when I'm first calling the verify_and_login method
First you need verify that the URL is signed by adding the middleware signed
You don't want that anoyone having the url /email/verify/{id}/{hash}/{user_id} able to access this ressource without the signature.
web.php
Route::get('/email/verify/{id}/{hash}/{user_id}','Auth\CustomVerifyController#login_and_verify')
->middleware('signed')
->name('verification.custom-verify');
Then you need to verify that the hash correspond the user_id and for that you can use a Request or a Middleware. I think the Request fits better since Laravel already uses a Request for this.
CustomEmailVerificationRequest.php
<?php
namespace App\Http\Requests;
use Illuminate\Auth\Events\Verified;
use Illuminate\Foundation\Http\FormRequest;
class EmailVerificationRequest extends FormRequest
{
public function authorize()
{
$user = User::findOrFail($this->route('id'));
if (! hash_equals((string) $this->route('hash'), sha1($user->getEmailForVerification()))) {
return false;
}
return true;
}
}
Finally you need to login with the user and set is email as verified
CustomVerifyController.php
public function login_and_verify(CustomEmailVerificationRequest $request)
{
$user = User::findOrFail($this->route('id'));
Auth::login($user);
$user->markEmailAsVerified();
event(new Verified($user));
...
}
[Edit to add addition feature from comments]
In order to have a middleware that verify the signed URL and resend automatically the verification email, you need to build a custom middleware.
ValidateSignatureAndResendEmailVerification.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Routing\Exceptions\InvalidSignatureException;
use URL;
class ValidateSignatureAndResendEmailVerification
{
public function handle($request, Closure $next, $relative = null)
{
if(! URL::hasCorrectSignature($request, $relative !== 'relative')( {
throw new InvalidSignatureException;
}
if (URL::signatureHasNotExpired()) {
return $next($request);
}
return redirect()->route('resend-email-confirmation');
}
}
Then you need to add the middleware to Kernel.php
Kernel.php
protected $routeMiddleware = [
...
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'signed.email' => \App\Http\Middleware\ValidateSignatureAndResendEmailVerification::class,
...
];
Then, don't forget to update your route with the new middleware
web.php
Route::get('/email/verify/{id}/{hash}/{user_id}','Auth\CustomVerifyController#login_and_verify')
->middleware('signed.email')
->name('verification.custom-verify');

Prevent role-specific users from accessing route

I have 2 roles, which is admin and user. Now when logging in, the admin goes to the dashboard route while the user goes to home. When user is logged in and changes the url to http://127.0.0.1:8000/dashboard it can access the admin's panel and I don't want that. How can I do achieve this?
PS. I'm new to Laravel
The good practice for this is usage of Middewares.
Create middlewares for admins and users (I'll do that only for admins, you can do that similarly for users):
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminMiddleware
{
public function handle($request, Closure $next)
{
if(Auth::check()){
// check auth user role (I don't know how you can implement this for yourself, this is just for me)
if(Auth::user()->role->name == 'admin'){
return $next($request);
} else {
return redirect()->route('admin.dashboard'); // for admins
}
}
return redirect()->route('main'); // for users
}
}
In "app/Http/Kernel.php" in $routeMiddleware array register that (add to end of that array).
'Admin' => \App\Http\Middleware\AdminMiddleware::class,
Now if you are using all requests in "routes/web.php" (actually I think it does), then you can use routes like this for admins:
// USER ROUTES
Route::get('/', 'FrontController#main')->name('main');
// ADMIN ROUTES
Route::group([
'as' => 'admin.',
'middleware' => [ 'Admin' ],
], function () {
Route::get('dashboard', 'AdminController#dashboard');
});
Refresh caches via "php artisan config:cache".
Try it!
Use middleware to admin route or inside the controller
like this:
Route::put('post/{id}', function ($id) {
//
})->middleware('role:editor');
or
Route::middleware(['auth', 'admin'])->group(function (){
Route::get('dashboard', 'HomeController#index')->name('home.index');
});
or inside the controller like this:
public function __construct()
{
$this->middleware(['auth', 'admin'])->except(['index']);
}
or you can use this for middleware roles.

return a json error in middleware?

I'm building an app and I'm using laravel5 as webAPI.
When the webAPI is in Maintenance Mode, I want to return a json error to app and I will get the status code in app to show a suitable message.
I rewrite the laravel CheckForMaintenanceMode for somereason and registed it in Kernel.
I write
if ($this->app->isDownForMaintenance()) {
$ip = $request->getClientIp();
$allowIp = "111.222.333.444";
if ($allowIp != $ip) {
return response()->json(['error' => "Maintenance!!"], 503);
}
}
return $next($request);
But I can get NOTHING in app side.I cannot get the message, the satus....
I writh the same code like return response()->json(['error' => "errormessage"], 422); in controller and I can get the message.status.. in app but I cannot do the same thing in a middleware.
why? how to do it?
This worked:
if ($this->app->isDownForMaintenance()) {
$ip = $request->getClientIp();
$allowIp = "111.222.333.444";
if ($allowIp != $ip) {
return response(['Maintenance'], 503);
}
}
return $next($request);
And not register the middleware in Kernel global HTTP middleware but put it in the route(api.php),like:
Route::group(['middleware' => 'maintenance'], function(){******}
I really donot know why but this worked for me.
Full example
public function handle($request, Closure $next)
{
if($request->token == "mytoken")
return $next($request);
else return response(['Token mismatch'],403);
}
Explanation
The response of a middleware
must be an instance of Symfony\Component\HttpFoundation\Response
so, for return a json, you have to do this
return response(['Token mismatch'],403);
The middleware must be registered in Kernel.php
The cleaner way to do it is to extend the
Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode class
and change it as per our needs and update the App\Http\Kernel.php like so..
App\Http\CustomMaintanceMiddleware.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
use Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode;
class CustomMaintanceMiddleware extends CheckForMaintenanceMode
{
public function handle($request, Closure $next)
{
if ($this->app->isDownForMaintenance()) {
return response(['Maintenance'], 503);
}
return $next($request);
}
}
Kernel.php
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class
];
TO
protected $middleware = [
\App\Http\CustomMaintanceMiddleware::class
];

Laravel login redirected you too many times

I have been struggling with this from quiet a time now, what i am trying is to redirect all the url's hit by non-logged in users to login page and it gives me this error, which I am sure is because it is creating a loop on /login URL. authentication is checking for authorized user in login page also. however I wish the login page should be an exception when checking the auth. I may be doing something wrong which I am not able to get. here goes my code.
routes.php
Route::post('login', 'Auth\AuthController#login');
Route::get('login' , 'Auth\AuthController#showLoginForm');
Route::get('/' , 'Auth\AuthController#showLoginForm');
kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'can' => \Illuminate\Foundation\Auth\Access\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'acl' => \App\Http\Middleware\CheckPermission::class,
];
Authenticate class
class Authenticate
{
public function handle($request, Closure $next, $guard = null) {
if (Auth::guard($guard)->guest()) {
if ($request->ajax() || $request->wantsJson()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('login');
}
}
return $next($request);
}
}
AuthController class
class AuthController extends Controller {
use AuthenticatesAndRegistersUsers, ThrottlesLogins;
protected $redirectTo = '/dashboard';
protected $loginPath = '/login';
protected $redirectPath = '/dashboard';
public function __construct(){
$this->middleware('auth', ['except' =>'login']);
/* I have been trying these many things to fix this, all in loss.
// $this->middleware('acl'); // To all methods
// $this->middleware('acl', ['only' => ['create', 'update']]);
// $this->middleware('guest', ['only' => ['/login']]);
// echo "Message"; exit;
// $this->middleware('auth');
// $this->middleware('auth', ['only' => ['login']]);
// $this->middleware('auth', ['only' => ['/login']]);
// $this->middleware('auth', ['except' => 'login']);
// $this->middleware('guest');
// $this->middleware('guest', ['only' => ['logout' , 'login', '/login', '/']]);
}
Please help me, It going all above my head, seems some sort of rocket science to me. well btw I am new to laravel and may be doing some silly thing around, apologies for that. Thanks in Advance.
You need add route login outside Laravel group:
routes.php
Route::auth();
Route::group(['middleware' => 'auth'], function () {
// All route your need authenticated
});
Aditionally, you can see yours route list using:
php artisan route:list
Why you are doing all this just to redirect every non-logged in user to login form?
i think you can just do this
Routes.php
Route::post('login', 'Auth\AuthController#login');
Route::get('login' , 'Auth\AuthController#showLoginForm');
Route::get('/' , 'Auth\AuthController#showLoginForm');
Route::group(['middleware' => 'auth'], function () {
// any route here will only be accessible for logged in users
});
and auth controller construct should be like this
AuthController
public function __construct()
{
$this->middleware('guest', ['except' => 'logout']);
}
The problem is with your routes.
When I enter and I am not logged out you send me to login(get) route. And as you are specifying the middleware in the construct function in the AuthController, every time a method of the AuthController is called, construct function is called again and sends you back at login.. and it repeats indefinitely.
like #mkmnstr say
The problem is with your routes.
When I enter and I am not logged out you send me to login(get) route. And as you are specifying the middleware in the construct function in the AuthController, every time a method of the AuthController is called, construct function is called again and sends you back at login.. and it repeats indefinitely.
to fix that u should add
Auth::logout();
Here
...
} else {
Auth::logout(); // user must logout before redirect them
return redirect()->guest('login');
}
...
If your working with custom middleware you must follow it's all rules
in my case, I have to define a custom route class in the web middleware group.
In the world of copy-paste sometime we make mistakes.
Middleware :
public function handle($request, Closure $next)
{
if(!isset(session('user'))){
return redirect('login');
}
return $next($request);
}
}
My Mistake in Kernel.php
if custom middleware class present in web $middlewareGroups will check condition 2 times so it will give error as: redirected you too many times
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\webUser::class, //Remove
],
protected $routeMiddleware = [
'webUser'=> \App\Http\Middleware\webUser::class //Keepit
]
I had same problem after creating my own route service provider. The problem was that when I tried to login, in first time login page showed and after entering credentials I encountered "redirected too many times" and redirected to my admin dashboard and login route!
the solution was: adding middleware "web" into my routes:
Route::middleware('web')->group(base_path('Admin/routes.php'));

Laravel route group middleware issue

I keep some laravel routes in the middleware auth group as:
Route::group(['middleware'=>'auth'],function(){
Route::controller('Activities', 'ActivitiesController');
Route::get('foo','FooController#getFoo');
.....
});
When I try to login to access these page, I am unable to login and url redirect to login page again and again. But If I use constructor as:
public function __construct()
{
$this->middleware('auth');
}
In those controllers It works perfectly. What is route group problem?
Route has a ::middleware class that you can use:
Routes > web.php
Route::middleware(['auth'])->group(function(){
Route::get('/activities', 'ActivitiesController#index');
});
You can also use Route::resource(); which I prefer. If you don't know what it does, here are the docs: https://laravel.com/docs/5.8/controllers#resource-controllers
This works for me , in route
Route::group(['middleware'=>'auth'],function(){
Route::controller('activities', 'ActivitiesController');
});
then controller
<?php namespace App\Http\Controllers;
class ActivitiesController extends Controller {
public function getIndex() {
return 'you are in;
}
}
on attempt to visit /activities I was redirected to login page , and on success back to \activities with 'you are in'.
In web.php:
$roleGeneral = role1.'~'.role2.'~'.role3.'~'.role4;
Route::group(['middleware' => ['permission.role:'.$roleGeneral]], function() {})
In Kernel.php:
protected $routeMiddleware = [...,
'permission.role' => \App\Http\Middleware\CheckPermission::class,
];
In CheckPermission.php:
public function handle($request, Closure $next, $role)
{
$roleArr = explode('~', $role);
$token = JWTAuth::getToken();
$user = JWTAuth::toUser($token);
$roleLogin = SysRoleModel::where('id', $user->role_id)->first();
if (in_array($roleLogin['name'], $roleArr)){
return $next($request);
}else{
return \Redirect::back()->withMessage('You are not authorized to access!');
}
}

Resources