Skip to content

Commit

Permalink
Rudimentry 2fa
Browse files Browse the repository at this point in the history
  • Loading branch information
MuchQuak committed Nov 19, 2024
1 parent 381cc9e commit 78ffc0e
Show file tree
Hide file tree
Showing 27 changed files with 971 additions and 18 deletions.
40 changes: 40 additions & 0 deletions app/Actions/Fortify/CreateNewUser.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Actions\Fortify;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\CreatesNewUsers;

class CreateNewUser implements CreatesNewUsers
{
use PasswordValidationRules;

/**
* Validate and create a newly registered user.
*
* @param array<string, string> $input
*/
public function create(array $input): User
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],
'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique(User::class),
],
'password' => $this->passwordRules(),
])->validate();

return User::create([
'name' => $input['name'],
'email' => $input['email'],
'password' => Hash::make($input['password']),
]);
}
}
18 changes: 18 additions & 0 deletions app/Actions/Fortify/PasswordValidationRules.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace App\Actions\Fortify;

use Illuminate\Validation\Rules\Password;

trait PasswordValidationRules
{
/**
* Get the validation rules used to validate passwords.
*
* @return array<int, \Illuminate\Contracts\Validation\Rule|array<mixed>|string>
*/
protected function passwordRules(): array
{
return ['required', 'string', Password::default(), 'confirmed'];
}
}
29 changes: 29 additions & 0 deletions app/Actions/Fortify/ResetUserPassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace App\Actions\Fortify;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\ResetsUserPasswords;

class ResetUserPassword implements ResetsUserPasswords
{
use PasswordValidationRules;

/**
* Validate and reset the user's forgotten password.
*
* @param array<string, string> $input
*/
public function reset(User $user, array $input): void
{
Validator::make($input, [
'password' => $this->passwordRules(),
])->validate();

$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
32 changes: 32 additions & 0 deletions app/Actions/Fortify/UpdateUserPassword.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Actions\Fortify;

use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;
use Laravel\Fortify\Contracts\UpdatesUserPasswords;

class UpdateUserPassword implements UpdatesUserPasswords
{
use PasswordValidationRules;

/**
* Validate and update the user's password.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'current_password' => ['required', 'string', 'current_password:web'],
'password' => $this->passwordRules(),
], [
'current_password.current_password' => __('The provided password does not match your current password.'),
])->validateWithBag('updatePassword');

$user->forceFill([
'password' => Hash::make($input['password']),
])->save();
}
}
58 changes: 58 additions & 0 deletions app/Actions/Fortify/UpdateUserProfileInformation.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace App\Actions\Fortify;

use App\Models\User;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;

class UpdateUserProfileInformation implements UpdatesUserProfileInformation
{
/**
* Validate and update the given user's profile information.
*
* @param array<string, string> $input
*/
public function update(User $user, array $input): void
{
Validator::make($input, [
'name' => ['required', 'string', 'max:255'],

'email' => [
'required',
'string',
'email',
'max:255',
Rule::unique('users')->ignore($user->id),
],
])->validateWithBag('updateProfileInformation');

if ($input['email'] !== $user->email &&
$user instanceof MustVerifyEmail) {
$this->updateVerifiedUser($user, $input);
} else {
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
])->save();
}
}

/**
* Update the given verified user's profile information.
*
* @param array<string, string> $input
*/
protected function updateVerifiedUser(User $user, array $input): void
{
$user->forceFill([
'name' => $input['name'],
'email' => $input['email'],
'email_verified_at' => null,
])->save();

$user->sendEmailVerificationNotification();
}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/MediaController.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class MediaController extends Controller{
* @return void
*/
public function __construct(){
parent::__construct();
//parent::__construct();
$this->rulesUpdate = $this->rulesInsert;
unset($this->rulesUpdate['originalUrl']);
}
Expand Down Expand Up @@ -522,4 +522,4 @@ private function getMimeType($url){
}
return $mimeType;
}
}
}
4 changes: 2 additions & 2 deletions app/Http/Controllers/TaxonomyController.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class TaxonomyController extends Controller{
* @return void
*/
public function __construct(){
parent::__construct();
//parent::__construct();
}

/**
Expand Down Expand Up @@ -282,4 +282,4 @@ public function showAllDescriptions($id, Request $request){
return response()->json($retObj);
}

}
}
3 changes: 2 additions & 1 deletion app/Models/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable {
use HasApiTokens, Notifiable, HasFactory;
use HasApiTokens, TwoFactorAuthenticatable, Notifiable, HasFactory;

protected $primaryKey = 'uid';
const CREATED_AT = 'initialTimestamp';
Expand Down
153 changes: 153 additions & 0 deletions app/Providers/FortifyServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php

namespace App\Providers;

use App\Actions\Fortify\CreateNewUser;
use App\Actions\Fortify\ResetUserPassword;
use App\Actions\Fortify\UpdateUserPassword;
use App\Actions\Fortify\UpdateUserProfileInformation;
use App\Models\User;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\MessageBag;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Hash;
use Laravel\Fortify\Contracts\PasswordConfirmedResponse;
use Laravel\Fortify\Contracts\RegisterResponse;
use Laravel\Fortify\Contracts\TwoFactorDisabledResponse;
use Laravel\Fortify\Contracts\LoginResponse;
use Laravel\Fortify\Fortify;
use Laravel\Fortify\Contracts\FailedPasswordConfirmationResponse;

class FortifyServiceProvider extends ServiceProvider {
/**
* Register any application services.
*/
public function register(): void {
$this->app->instance(LoginResponse::class, new class implements LoginResponse {
public function toResponse($request) {
return response(view('pages/home'))
->header('HX-Replace-URL', config('fortify.home'))
->header('HX-Retarget', 'body')
->header('HX-Boosted', 'true');
}
});

$this->app->instance(RegisterResponse::class, new class implements RegisterResponse {
public function toResponse($request) {
return response(view('pages/home'))
->header('HX-Replace-URL', config('fortify.home'))
->header('HX-Retarget', 'body')
->header('HX-Boosted', 'true');
}
});

$this->app->instance(TwoFactorDisabledResponse::class, new class implements TwoFactorDisabledResponse {
public function toResponse($request) {
return response(view('pages/user/profile'))
->header('HX-Retarget', 'body')
->header('HX-Boosted', 'true');
}
});

$this->app->instance(PasswordConfirmedResponse::class, new class implements PasswordConfirmedResponse {
public function toResponse($request) {
return response(view('/pages/user/profile'))
->header('HX-Retarget', 'body')
->header('HX-Request', 'true');
}
});

$this->app->instance(FailedPasswordConfirmationResponse::class, new class implements FailedPasswordConfirmationResponse {
public function toResponse($request) {

$message = __('The provided password was incorrect.');

return response(view('/pages/auth/confirm-password',
[ 'errors' => new MessageBag([$message]) ]
))
->header('HX-Retarget', 'body')
->header('HX-Request', 'true');
}
});
}

/**
* Bootstrap any application services.
*/
public function boot(): void {

Fortify::loginView(function (Request $request) {
//If Request Redirect on to self then only send fragment. This is for htmx to do the correct swap
//
// If its on current page ignore target
if($request->headers->get('hx-request') && !$request->headers->get('hx-target')) {
return view('pages/login')->fragment('form');
}

return response(view('pages/login'))->header('HX-Replace-URL', '/login');
});

Fortify::registerView(function (Request $request) {
//If Request Redirect on to self then only send fragment. This is for htmx to do the correct swap
if($request->headers->get('hx-request') && !$request->headers->get('hx-target')) {
return view('pages/signup')->fragment('form');
}

return response(view('pages/signup'))->header('HX-Replace-URL', '/register');
});

Fortify::confirmPasswordView(function () {
return response(view('pages/auth/confirm-password'))
//->header('HX-Replace-URL', '/auth/confirm-password')
->header('HX-Retarget', 'body')
->header('HX-Request', 'true');
});

Fortify::twoFactorChallengeView(function () {
return response(view('pages/auth/two-factor-challenge'))
->header('HX-Request', 'true')
->header('HX-Replace-URL', '/two-factor-challenge')
->header('HX-Boosted', 'true')
->header('HX-Retarget', 'body');
});

Fortify::authenticateUsing(function (Request $request) {
$user = User::where('email', $request->email)->first();

if ($user &&
Hash::check($request->password, $user->password)) {
return $user;
}

/*
//Check Old Password
$result = DB::select('
SELECT uid
FROM users
WHERE (old_password = CONCAT(\'*\', UPPER(SHA1(UNHEX(SHA1(?)))))) AND
email = ?',
[$request->password, $request->email]
);
*/

});

Fortify::createUsersUsing(CreateNewUser::class);
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);

RateLimiter::for('login', function (Request $request) {
$throttleKey = Str::transliterate(Str::lower($request->input(Fortify::username())).'|'.$request->ip());

return Limit::perMinute(5)->by($throttleKey);
});

RateLimiter::for('two-factor', function (Request $request) {
return Limit::perMinute(5)->by($request->session()->get('login.id'));
});
}
}
5 changes: 2 additions & 3 deletions app/Providers/RouteServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,15 @@
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
class RouteServiceProvider extends ServiceProvider {
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/home';
public const HOME = '/';

/**
* Define your route model bindings, pattern filters, and other route configuration.
Expand Down
Empty file modified bootstrap/cache/.gitignore
100644 → 100755
Empty file.
Loading

0 comments on commit 78ffc0e

Please sign in to comment.