diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index c61c3241da..bfb98ede79 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -17,6 +17,7 @@ use App\Http\Middleware\EnsureTeamMcpEnabled; use App\Http\Middleware\EnsureTokenBelongsToCurrentTeamMember; use App\Http\Middleware\PreventRequestsDuringMaintenance; use App\Http\Middleware\RedirectIfAuthenticated; +use App\Http\Middleware\RequirePassword; use App\Http\Middleware\TrimStrings; use App\Http\Middleware\TrustProxies; use App\Http\Middleware\ValidateSignature; @@ -24,7 +25,6 @@ use App\Http\Middleware\VerifyCsrfToken; use Illuminate\Auth\Middleware\AuthenticateWithBasicAuth; use Illuminate\Auth\Middleware\Authorize; use Illuminate\Auth\Middleware\EnsureEmailIsVerified; -use Illuminate\Auth\Middleware\RequirePassword; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Foundation\Http\Kernel as HttpKernel; use Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull; diff --git a/app/Http/Middleware/RequirePassword.php b/app/Http/Middleware/RequirePassword.php new file mode 100644 index 0000000000..589bb5de7c --- /dev/null +++ b/app/Http/Middleware/RequirePassword.php @@ -0,0 +1,22 @@ +user()?->requiresPasswordConfirmation() === false) { + return false; + } + + return parent::shouldConfirmPassword($request, $passwordTimeoutSeconds); + } +} diff --git a/tests/Feature/OauthPasswordConfirmationTest.php b/tests/Feature/OauthPasswordConfirmationTest.php index 528e2565b7..92cf0afcc2 100644 --- a/tests/Feature/OauthPasswordConfirmationTest.php +++ b/tests/Feature/OauthPasswordConfirmationTest.php @@ -3,6 +3,7 @@ use App\Models\InstanceSettings; use App\Models\OauthIdentity; use App\Models\OauthSetting; +use App\Models\Team; use App\Models\User; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Support\Facades\Blade; @@ -173,3 +174,35 @@ describe('confirmation modal', function () { ->and($html)->not->toContain('Confirm with Google'); }); }); + +describe('two-factor setup', function () { + beforeEach(function () { + $this->team = Team::factory()->create(['show_boarding' => false]); + }); + + it('lets a user with a linked identity and no password enable 2fa without a password confirmation', function () { + $user = User::factory()->create(); + $user->forceFill(['password' => null])->save(); + $this->team->members()->attach($user, ['role' => 'owner']); + linkGoogleIdentity($user, 'google-id'); + + $this->actingAs($user) + ->withSession(['currentTeam' => $this->team]) + ->post(route('two-factor.enable')) + ->assertSessionHas('status', 'two-factor-authentication-enabled'); + + expect($user->fresh()->two_factor_secret)->not->toBeNull(); + }); + + it('still asks a password user without a linked identity to confirm the password', function () { + $user = User::factory()->create(['password' => Hash::make('secret-password')]); + $this->team->members()->attach($user, ['role' => 'owner']); + + $this->actingAs($user) + ->withSession(['currentTeam' => $this->team]) + ->post(route('two-factor.enable')) + ->assertRedirect(route('password.confirm')); + + expect($user->fresh()->two_factor_secret)->toBeNull(); + }); +});