Prechádzať zdrojové kódy

Force logout on routes not watched by the KickOutInactiveUser middleware

Bubka 9 mesiacov pred
rodič
commit
528a3be458

+ 3 - 1
app/Http/Kernel.php

@@ -73,12 +73,14 @@ class Kernel extends HttpKernel
     protected $middlewareAliases = [
         'auth'                          => \App\Http\Middleware\Authenticate::class,
         'admin'                         => \App\Http\Middleware\AdminOnly::class,
-        'guest'                         => \App\Http\Middleware\RejectIfAuthenticated::class,
+        'rejectIfAuthenticated'         => \App\Http\Middleware\RejectIfAuthenticated::class,
         'throttle'                      => \Illuminate\Routing\Middleware\ThrottleRequests::class,
         'rejectIfDemoMode'              => \App\Http\Middleware\RejectIfDemoMode::class,
         'rejectIfReverseProxy'          => \App\Http\Middleware\RejectIfReverseProxy::class,
         'RejectIfSsoOnlyAndNotForAdmin' => \App\Http\Middleware\RejectIfSsoOnlyAndNotForAdmin::class,
         'cache.headers'                 => \Illuminate\Http\Middleware\SetCacheHeaders::class,
+        'kickOutInactiveUser'           => \App\Http\Middleware\KickOutInactiveUser::class,
+        'forceLogout'                   => \App\Http\Middleware\ForceLogout::class,
         // 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
         // 'signed' => \App\Http\Middleware\ValidateSignature::class,
     ];

+ 25 - 0
app/Http/Middleware/ForceLogout.php

@@ -0,0 +1,25 @@
+<?php
+
+namespace App\Http\Middleware;
+
+use Closure;
+use Illuminate\Support\Facades\Auth;
+
+class ForceLogout
+{
+    /**
+     * Handle an incoming request.
+     *
+     * @param  \Illuminate\Http\Request  $request
+     * @param  string  $guards
+     * @return mixed
+     */
+    public function handle($request, Closure $next, ...$guards)
+    {
+        if (Auth::user() != null) {
+            Auth::guard('web-guard')->logoutCurrentDevice();
+        }
+
+        return $next($request);
+    }
+}

+ 23 - 20
app/Listeners/Authentication/LogoutListener.php

@@ -43,27 +43,30 @@ class LogoutListener extends AbstractAccessListener
          * @var \App\Models\User
          */
         $user = $event->user;
-        $ip   = config('2fauth.proxy_headers.forIp')
-            ? $this->request->header(config('2fauth.proxy_headers.forIp'), $this->request->ip())
-            : $this->request->ip();
-        $userAgent = $this->request->userAgent();
-        $log       = $user->authentications()
-            ->whereIpAddress($ip)
-            ->whereUserAgent($userAgent)
-            ->whereGuard($event->guard)
-            ->orderByDesc('login_at')
-            ->first();
 
-        if (! $log) {
-            $log = new AuthLog([
-                'ip_address'   => $ip,
-                'user_agent'   => $userAgent,
-                'guard'        => $event->guard,
-                'login_method' => $this->loginMethod(),
-            ]);
-        }
+        if ($user != null) {
+            $ip   = config('2fauth.proxy_headers.forIp')
+                ? $this->request->header(config('2fauth.proxy_headers.forIp'), $this->request->ip())
+                : $this->request->ip();
+            $userAgent = $this->request->userAgent();
+            $log       = $user->authentications()
+                ->whereIpAddress($ip)
+                ->whereUserAgent($userAgent)
+                ->whereGuard($event->guard)
+                ->orderByDesc('login_at')
+                ->first();
+
+            if (! $log) {
+                $log = new AuthLog([
+                    'ip_address'   => $ip,
+                    'user_agent'   => $userAgent,
+                    'guard'        => $event->guard,
+                    'login_method' => $this->loginMethod(),
+                ]);
+            }
 
-        $log->logout_at = now();
-        $user->authentications()->save($log);
+            $log->logout_at = now();
+            $user->authentications()->save($log);  
+        }
     }
 }

+ 5 - 4
routes/web.php

@@ -34,8 +34,9 @@ use Laravel\Passport\Http\Controllers\PersonalAccessTokenController;
 
 /**
  * Routes that only work for unauthenticated user (return an error otherwise)
+ * 'kickOutInactiveUser', 
  */
-Route::group(['middleware' => ['guest', 'rejectIfDemoMode', 'RejectIfSsoOnlyAndNotForAdmin']], function () {
+Route::group(['middleware' => ['rejectIfDemoMode', 'RejectIfSsoOnlyAndNotForAdmin', 'forceLogout']], function () {
     Route::post('user', [RegisterController::class, 'register'])->name('user.register');
     Route::post('user/password/lost', [ForgotPasswordController::class, 'sendResetLinkEmail'])->name('user.password.lost');
     Route::post('user/password/reset', [ResetPasswordController::class, 'reset'])->name('password.reset');
@@ -46,15 +47,15 @@ Route::group(['middleware' => ['guest', 'rejectIfDemoMode', 'RejectIfSsoOnlyAndN
 /**
  * Routes that can be requested max 10 times per minute by the same IP
  */
-Route::group(['middleware' => ['rejectIfDemoMode', 'throttle:10,1', 'RejectIfSsoOnlyAndNotForAdmin']], function () {
+Route::group(['middleware' => ['rejectIfDemoMode', 'throttle:10,1', 'RejectIfSsoOnlyAndNotForAdmin', 'forceLogout']], function () {
     Route::post('webauthn/recover', [WebAuthnRecoveryController::class, 'recover'])->name('webauthn.recover');
 });
 
 /**
  * Routes that only work for unauthenticated user (return an error otherwise)
- * that can be requested max 10 times per minute by the same IP
+ * that can be requested max 10 times per minute by the same IP 'kickOutInactiveUser', 
  */
-Route::group(['middleware' => ['guest', 'throttle:10,1']], function () {
+Route::group(['middleware' => ['forceLogout', 'throttle:10,1']], function () {
     Route::post('user/login', [LoginController::class, 'login'])->name('user.login')->middleware('RejectIfSsoOnlyAndNotForAdmin');
     Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])->name('webauthn.login')->middleware('RejectIfSsoOnlyAndNotForAdmin');
 

+ 2 - 2
tests/Feature/Http/Auth/ForgotPasswordControllerTest.php

@@ -95,7 +95,7 @@ class ForgotPasswordControllerTest extends FeatureTestCase
     }
 
     #[Test]
-    public function test_submit_email_password_request_when_authenticated_returns_bad_request()
+    public function test_submit_email_password_request_when_authenticated_returns_ok()
     {
         /**
          * @var \App\Models\User|\Illuminate\Contracts\Auth\Authenticatable
@@ -106,7 +106,7 @@ class ForgotPasswordControllerTest extends FeatureTestCase
             ->json('POST', '/user/password/lost', [
                 'email' => $user->email,
             ])
-            ->assertStatus(400)
+            ->assertStatus(200)
             ->assertJsonStructure([
                 'message',
             ]);

+ 1 - 4
tests/Feature/Http/Auth/LoginTest.php

@@ -250,10 +250,7 @@ class LoginTest extends FeatureTestCase
                 'email'    => $this->user->email,
                 'password' => self::PASSWORD,
             ])
-            ->assertStatus(400)
-            ->assertJsonStructure([
-                'message',
-            ]);
+            ->assertStatus(200);
     }
 
     #[Test]

+ 1 - 4
tests/Feature/Http/Auth/WebAuthnLoginControllerTest.php

@@ -275,10 +275,7 @@ class WebAuthnLoginControllerTest extends FeatureTestCase
             ->assertOk();
 
         $this->json('POST', '/webauthn/login', self::ASSERTION_RESPONSE)
-            ->assertStatus(400)
-            ->assertJsonStructure([
-                'message',
-            ]);
+            ->assertStatus(200);;
     }
 
     #[Test]