feature: authentication using fortify and sanctum's cookie based SPA authentication
This commit is contained in:
parent
b538bf8fee
commit
19a05a1a5b
6
.idea/neoban.iml
generated
6
.idea/neoban.iml
generated
@ -34,7 +34,6 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/ai" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/framework" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/pail" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/pint" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/prompts" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/sanctum" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/serializable-closure" />
|
||||
@ -139,6 +138,11 @@
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/webmozart/assert" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/storage/app" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/storage/framework" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/bacon/bacon-qr-code" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/dasprid/enum" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/laravel/fortify" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/paragonie/constant_time_encoding" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/backend/vendor/pragmarx/google2fa" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
||||
14
.idea/php.xml
generated
14
.idea/php.xml
generated
@ -1,5 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="LaravelPint">
|
||||
<laravel_pint_settings>
|
||||
<LaravelPintConfiguration tool_path="$PROJECT_DIR$/../.config/composer/vendor/bin/pint" />
|
||||
</laravel_pint_settings>
|
||||
</component>
|
||||
<component name="MessDetectorOptionsConfiguration">
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
@ -10,6 +15,9 @@
|
||||
<option name="highlightLevel" value="WARNING" />
|
||||
<option name="transferred" value="true" />
|
||||
</component>
|
||||
<component name="PhpExternalFormatter">
|
||||
<option name="externalFormatter" value="LARAVEL_PINT" />
|
||||
</component>
|
||||
<component name="PhpIncludePathManager">
|
||||
<include_path>
|
||||
<path value="$PROJECT_DIR$/backend/vendor/nikic/php-parser" />
|
||||
@ -50,7 +58,6 @@
|
||||
<path value="$PROJECT_DIR$/backend/vendor/laravel/prompts" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/laravel/framework" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/graham-campbell/result-type" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/laravel/pint" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/ta-tikoma/phpunit-architecture-test" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/doctrine/deprecations" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/psr/container" />
|
||||
@ -141,6 +148,11 @@
|
||||
<path value="$PROJECT_DIR$/backend/vendor/laravel/ai" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/aws/aws-sdk-php" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/aws/aws-crt-php" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/laravel/fortify" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/dasprid/enum" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/paragonie/constant_time_encoding" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/pragmarx/google2fa" />
|
||||
<path value="$PROJECT_DIR$/backend/vendor/bacon/bacon-qr-code" />
|
||||
</include_path>
|
||||
</component>
|
||||
<component name="PhpProjectSharedConfiguration" php_language_level="8.3">
|
||||
|
||||
@ -3,6 +3,7 @@ APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
FRONTEND_URL="http://localhost:4200,http://127.0.0.1:4200"
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
@ -31,7 +32,7 @@ SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
SESSION_DOMAIN=localhost
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
|
||||
43
backend/app/Actions/Fortify/CreateNewUser.php
Normal file
43
backend/app/Actions/Fortify/CreateNewUser.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Laravel\Fortify\Contracts\CreatesNewUsers;
|
||||
|
||||
class CreateNewUser implements CreatesNewUsers
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and create a newly registered user.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
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']),
|
||||
]);
|
||||
}
|
||||
}
|
||||
19
backend/app/Actions/Fortify/PasswordValidationRules.php
Normal file
19
backend/app/Actions/Fortify/PasswordValidationRules.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
trait PasswordValidationRules
|
||||
{
|
||||
/**
|
||||
* Get the validation rules used to validate passwords.
|
||||
*
|
||||
* @return array<int, Rule|array<mixed>|string>
|
||||
*/
|
||||
protected function passwordRules(): array
|
||||
{
|
||||
return ['required', 'string', Password::default(), 'confirmed'];
|
||||
}
|
||||
}
|
||||
32
backend/app/Actions/Fortify/ResetUserPassword.php
Normal file
32
backend/app/Actions/Fortify/ResetUserPassword.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Laravel\Fortify\Contracts\ResetsUserPasswords;
|
||||
|
||||
class ResetUserPassword implements ResetsUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and reset the user's forgotten password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function reset(User $user, array $input): void
|
||||
{
|
||||
Validator::make($input, [
|
||||
'password' => $this->passwordRules(),
|
||||
])->validate();
|
||||
|
||||
$user->forceFill([
|
||||
'password' => Hash::make($input['password']),
|
||||
])->save();
|
||||
}
|
||||
}
|
||||
35
backend/app/Actions/Fortify/UpdateUserPassword.php
Normal file
35
backend/app/Actions/Fortify/UpdateUserPassword.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserPasswords;
|
||||
|
||||
class UpdateUserPassword implements UpdatesUserPasswords
|
||||
{
|
||||
use PasswordValidationRules;
|
||||
|
||||
/**
|
||||
* Validate and update the user's password.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
61
backend/app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
61
backend/app/Actions/Fortify/UpdateUserProfileInformation.php
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Fortify;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Laravel\Fortify\Contracts\UpdatesUserProfileInformation;
|
||||
|
||||
class UpdateUserProfileInformation implements UpdatesUserProfileInformation
|
||||
{
|
||||
/**
|
||||
* Validate and update the given user's profile information.
|
||||
*
|
||||
* @param array<string, string> $input
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,9 @@
|
||||
use App\Http\Requests\SocialMediaPostRequest;
|
||||
use App\Services\SocialMediaService;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Routing\Attributes\Controllers\Middleware;
|
||||
|
||||
#[Middleware('auth:sanctum')]
|
||||
class SocialMediaPostController extends Controller
|
||||
{
|
||||
/**
|
||||
@ -17,6 +19,7 @@ public function generate(
|
||||
): JsonResponse {
|
||||
$prompt = $request->input('prompt');
|
||||
$response = $socialMediaService->generatePostWithImage($prompt);
|
||||
|
||||
return response()->json([
|
||||
'post' => $response->post,
|
||||
'image_prompt' => $response->image,
|
||||
|
||||
@ -16,7 +16,7 @@
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable, HasApiTokens;
|
||||
use HasApiTokens, HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Support\Facades\Date;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
@ -19,6 +23,28 @@ public function register(): void
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
$this->configureDefaults();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure default behaviors for production-ready applications.
|
||||
*/
|
||||
protected function configureDefaults(): void
|
||||
{
|
||||
Date::use(CarbonImmutable::class);
|
||||
|
||||
DB::prohibitDestructiveCommands(
|
||||
app()->isProduction(),
|
||||
);
|
||||
|
||||
Password::defaults(fn (): ?Password => app()->isProduction()
|
||||
? Password::min(12)
|
||||
->mixedCase()
|
||||
->letters()
|
||||
->numbers()
|
||||
->symbols()
|
||||
->uncompromised()
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
48
backend/app/Providers/FortifyServiceProvider.php
Normal file
48
backend/app/Providers/FortifyServiceProvider.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?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 Illuminate\Cache\RateLimiting\Limit;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Str;
|
||||
use Laravel\Fortify\Actions\RedirectIfTwoFactorAuthenticatable;
|
||||
use Laravel\Fortify\Fortify;
|
||||
|
||||
class FortifyServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Register any application services.
|
||||
*/
|
||||
public function register(): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*/
|
||||
public function boot(): void
|
||||
{
|
||||
Fortify::createUsersUsing(CreateNewUser::class);
|
||||
Fortify::updateUserProfileInformationUsing(UpdateUserProfileInformation::class);
|
||||
Fortify::updateUserPasswordsUsing(UpdateUserPassword::class);
|
||||
Fortify::resetUserPasswordsUsing(ResetUserPassword::class);
|
||||
Fortify::redirectUserForTwoFactorAuthenticationUsing(RedirectIfTwoFactorAuthenticatable::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'));
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,15 @@
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware): void {
|
||||
//
|
||||
$middleware->statefulApi();
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions): void {
|
||||
//
|
||||
$exceptions->shouldRenderJsonWhen(function (Request $request, Throwable $e) {
|
||||
// Force JSON for all API routes AND Fortify authentication routes
|
||||
if ($request->is('api/*') || $request->is('register') || $request->is('login') || $request->is('logout')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $request->expectsJson();
|
||||
});
|
||||
})->create();
|
||||
|
||||
@ -1,7 +1,9 @@
|
||||
<?php
|
||||
|
||||
use App\Providers\AppServiceProvider;
|
||||
use App\Providers\FortifyServiceProvider;
|
||||
|
||||
return [
|
||||
AppServiceProvider::class,
|
||||
FortifyServiceProvider::class,
|
||||
];
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"require": {
|
||||
"php": "^8.3",
|
||||
"laravel/ai": "^0.6.3",
|
||||
"laravel/fortify": "^1.36",
|
||||
"laravel/framework": "^13.0",
|
||||
"laravel/sanctum": "^4.0",
|
||||
"laravel/tinker": "^3.0"
|
||||
@ -18,7 +19,6 @@
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
"laravel/pail": "^1.2.5",
|
||||
"laravel/pint": "^1.27",
|
||||
"mockery/mockery": "^1.6",
|
||||
"nunomaduro/collision": "^8.6",
|
||||
"pestphp/pest": "^4.6",
|
||||
|
||||
359
backend/composer.lock
generated
359
backend/composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "31337405f959f523455e59893a55ca54",
|
||||
"content-hash": "ce000f2979a5d252cefadab87899a5fc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
@ -157,6 +157,61 @@
|
||||
},
|
||||
"time": "2026-04-23T18:11:11+00:00"
|
||||
},
|
||||
{
|
||||
"name": "bacon/bacon-qr-code",
|
||||
"version": "v3.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Bacon/BaconQrCode.git",
|
||||
"reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Bacon/BaconQrCode/zipball/4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2",
|
||||
"reference": "4da2233e72eeecd9be3b62e0dc2cc9ed8e2e31c2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"dasprid/enum": "^1.0.3",
|
||||
"ext-iconv": "*",
|
||||
"php": "^8.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"phly/keep-a-changelog": "^2.12",
|
||||
"phpunit/phpunit": "^10.5.11 || ^11.0.4",
|
||||
"spatie/phpunit-snapshot-assertions": "^5.1.5",
|
||||
"spatie/pixelmatch-php": "^1.2.0",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-imagick": "to generate QR code images"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"BaconQrCode\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "BaconQrCode is a QR code generator for PHP.",
|
||||
"homepage": "https://github.com/Bacon/BaconQrCode",
|
||||
"support": {
|
||||
"issues": "https://github.com/Bacon/BaconQrCode/issues",
|
||||
"source": "https://github.com/Bacon/BaconQrCode/tree/v3.1.1"
|
||||
},
|
||||
"time": "2026-04-05T21:06:35+00:00"
|
||||
},
|
||||
{
|
||||
"name": "brick/math",
|
||||
"version": "0.14.8",
|
||||
@ -286,6 +341,56 @@
|
||||
],
|
||||
"time": "2024-02-09T16:56:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dasprid/enum",
|
||||
"version": "1.0.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DASPRiD/Enum.git",
|
||||
"reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/DASPRiD/Enum/zipball/b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
|
||||
"reference": "b5874fa9ed0043116c72162ec7f4fb50e02e7cce",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1 <9.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "*"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"DASPRiD\\Enum\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-2-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ben Scholzen 'DASPRiD'",
|
||||
"email": "mail@dasprids.de",
|
||||
"homepage": "https://dasprids.de/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "PHP 7.1 enum implementation",
|
||||
"keywords": [
|
||||
"enum",
|
||||
"map"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/DASPRiD/Enum/issues",
|
||||
"source": "https://github.com/DASPRiD/Enum/tree/1.0.7"
|
||||
},
|
||||
"time": "2025-09-16T12:23:56+00:00"
|
||||
},
|
||||
{
|
||||
"name": "dflydev/dot-access-data",
|
||||
"version": "v3.0.3",
|
||||
@ -1272,6 +1377,69 @@
|
||||
},
|
||||
"time": "2026-04-22T21:16:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/fortify",
|
||||
"version": "v1.36.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/fortify.git",
|
||||
"reference": "b36e0782e6f5f6cfbab34327895a63b7c4c031f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/fortify/zipball/b36e0782e6f5f6cfbab34327895a63b7c4c031f9",
|
||||
"reference": "b36e0782e6f5f6cfbab34327895a63b7c4c031f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"bacon/bacon-qr-code": "^3.0",
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^10.0|^11.0|^12.0|^13.0",
|
||||
"illuminate/support": "^10.0|^11.0|^12.0|^13.0",
|
||||
"php": "^8.1",
|
||||
"pragmarx/google2fa": "^9.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"orchestra/testbench": "^8.36|^9.15|^10.8|^11.0",
|
||||
"phpstan/phpstan": "^1.10"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Fortify\\FortifyServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Fortify\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Backend controllers and scaffolding for Laravel authentication.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"laravel"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/fortify/issues",
|
||||
"source": "https://github.com/laravel/fortify"
|
||||
},
|
||||
"time": "2026-03-20T20:13:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/framework",
|
||||
"version": "v13.6.0",
|
||||
@ -2883,6 +3051,75 @@
|
||||
],
|
||||
"time": "2026-02-16T23:10:27+00:00"
|
||||
},
|
||||
{
|
||||
"name": "paragonie/constant_time_encoding",
|
||||
"version": "v3.1.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/paragonie/constant_time_encoding.git",
|
||||
"reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
|
||||
"reference": "d5b01a39b3415c2cd581d3bd3a3575c1ebbd8e77",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8"
|
||||
},
|
||||
"require-dev": {
|
||||
"infection/infection": "^0",
|
||||
"nikic/php-fuzzer": "^0",
|
||||
"phpunit/phpunit": "^9|^10|^11",
|
||||
"vimeo/psalm": "^4|^5|^6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"ParagonIE\\ConstantTime\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Paragon Initiative Enterprises",
|
||||
"email": "security@paragonie.com",
|
||||
"homepage": "https://paragonie.com",
|
||||
"role": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Steve 'Sc00bz' Thomas",
|
||||
"email": "steve@tobtu.com",
|
||||
"homepage": "https://www.tobtu.com",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Constant-time Implementations of RFC 4648 Encoding (Base-64, Base-32, Base-16)",
|
||||
"keywords": [
|
||||
"base16",
|
||||
"base32",
|
||||
"base32_decode",
|
||||
"base32_encode",
|
||||
"base64",
|
||||
"base64_decode",
|
||||
"base64_encode",
|
||||
"bin2hex",
|
||||
"encoding",
|
||||
"hex",
|
||||
"hex2bin",
|
||||
"rfc4648"
|
||||
],
|
||||
"support": {
|
||||
"email": "info@paragonie.com",
|
||||
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
|
||||
"source": "https://github.com/paragonie/constant_time_encoding"
|
||||
},
|
||||
"time": "2025-09-24T15:06:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpoption/phpoption",
|
||||
"version": "1.9.5",
|
||||
@ -2958,6 +3195,58 @@
|
||||
],
|
||||
"time": "2025-12-27T19:41:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pragmarx/google2fa",
|
||||
"version": "v9.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/antonioribeiro/google2fa.git",
|
||||
"reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/antonioribeiro/google2fa/zipball/e6bc62dd6ae83acc475f57912e27466019a1f2cf",
|
||||
"reference": "e6bc62dd6ae83acc475f57912e27466019a1f2cf",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"paragonie/constant_time_encoding": "^1.0|^2.0|^3.0",
|
||||
"php": "^7.1|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpstan/phpstan": "^1.9",
|
||||
"phpunit/phpunit": "^7.5.15|^8.5|^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"PragmaRX\\Google2FA\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Antonio Carlos Ribeiro",
|
||||
"email": "acr@antoniocarlosribeiro.com",
|
||||
"role": "Creator & Designer"
|
||||
}
|
||||
],
|
||||
"description": "A One Time Password Authentication package, compatible with Google Authenticator.",
|
||||
"keywords": [
|
||||
"2fa",
|
||||
"Authentication",
|
||||
"Two Factor Authentication",
|
||||
"google2fa"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/antonioribeiro/google2fa/issues",
|
||||
"source": "https://github.com/antonioribeiro/google2fa/tree/v9.0.0"
|
||||
},
|
||||
"time": "2025-09-19T22:51:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "psr/clock",
|
||||
"version": "1.0.0",
|
||||
@ -6833,74 +7122,6 @@
|
||||
},
|
||||
"time": "2026-02-09T13:44:54+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/pint",
|
||||
"version": "v1.29.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/pint.git",
|
||||
"reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/pint/zipball/0770e9b7fafd50d4586881d456d6eb41c9247a80",
|
||||
"reference": "0770e9b7fafd50d4586881d456d6eb41c9247a80",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"ext-mbstring": "*",
|
||||
"ext-tokenizer": "*",
|
||||
"ext-xml": "*",
|
||||
"php": "^8.2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"friendsofphp/php-cs-fixer": "^3.95.1",
|
||||
"illuminate/view": "^12.56.0",
|
||||
"larastan/larastan": "^3.9.6",
|
||||
"laravel-zero/framework": "^12.1.0",
|
||||
"mockery/mockery": "^1.6.12",
|
||||
"nunomaduro/termwind": "^2.4.0",
|
||||
"pestphp/pest": "^3.8.6",
|
||||
"shipfastlabs/agent-detector": "^1.1.3"
|
||||
},
|
||||
"bin": [
|
||||
"builds/pint"
|
||||
],
|
||||
"type": "project",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"App\\": "app/",
|
||||
"Database\\Seeders\\": "database/seeders/",
|
||||
"Database\\Factories\\": "database/factories/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "enunomaduro@gmail.com"
|
||||
}
|
||||
],
|
||||
"description": "An opinionated code formatter for PHP.",
|
||||
"homepage": "https://laravel.com",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"format",
|
||||
"formatter",
|
||||
"lint",
|
||||
"linter",
|
||||
"php"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/pint/issues",
|
||||
"source": "https://github.com/laravel/pint"
|
||||
},
|
||||
"time": "2026-04-20T15:26:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mockery/mockery",
|
||||
"version": "1.6.12",
|
||||
|
||||
34
backend/config/cors.php
Normal file
34
backend/config/cors.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Cross-Origin Resource Sharing (CORS) Configuration
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure your settings for cross-origin resource sharing
|
||||
| or "CORS". This determines what cross-origin operations may execute
|
||||
| in web browsers. You are free to adjust these settings as needed.
|
||||
|
|
||||
| To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
|
||||
|
|
||||
*/
|
||||
|
||||
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout', 'register'],
|
||||
|
||||
'allowed_methods' => ['*'],
|
||||
|
||||
'allowed_origins' => explode(',', env('FRONTEND_URL', 'http://localhost:4200')),
|
||||
|
||||
'allowed_origins_patterns' => [],
|
||||
|
||||
'allowed_headers' => ['*'],
|
||||
|
||||
'exposed_headers' => [],
|
||||
|
||||
'max_age' => 0,
|
||||
|
||||
'supports_credentials' => true,
|
||||
|
||||
];
|
||||
159
backend/config/fortify.php
Normal file
159
backend/config/fortify.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
use Laravel\Fortify\Features;
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fortify Guard
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which authentication guard Fortify will use while
|
||||
| authenticating users. This value should correspond with one of your
|
||||
| guards that is already present in your "auth" configuration file.
|
||||
|
|
||||
*/
|
||||
|
||||
'guard' => 'web',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fortify Password Broker
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which password broker Fortify can use when a user
|
||||
| is resetting their password. This configured value should match one
|
||||
| of your password brokers setup in your "auth" configuration file.
|
||||
|
|
||||
*/
|
||||
|
||||
'passwords' => 'users',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Username / Email
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value defines which model attribute should be considered as your
|
||||
| application's "username" field. Typically, this might be the email
|
||||
| address of the users but you are free to change this value here.
|
||||
|
|
||||
| Out of the box, Fortify expects forgot password and reset password
|
||||
| requests to have a field named 'email'. If the application uses
|
||||
| another name for the field you may define it below as needed.
|
||||
|
|
||||
*/
|
||||
|
||||
'username' => 'email',
|
||||
|
||||
'email' => 'email',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Lowercase Usernames
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This value defines whether usernames should be lowercased before saving
|
||||
| them in the database, as some database system string fields are case
|
||||
| sensitive. You may disable this for your application if necessary.
|
||||
|
|
||||
*/
|
||||
|
||||
'lowercase_usernames' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Home Path
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may configure the path where users will get redirected during
|
||||
| authentication or password reset when the operations are successful
|
||||
| and the user is authenticated. You are free to change this value.
|
||||
|
|
||||
*/
|
||||
|
||||
'home' => '/home',
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fortify Routes Prefix / Subdomain
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which prefix Fortify will assign to all the routes
|
||||
| that it registers with the application. If necessary, you may change
|
||||
| subdomain under which all of the Fortify routes will be available.
|
||||
|
|
||||
*/
|
||||
|
||||
'prefix' => '',
|
||||
|
||||
'domain' => null,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Fortify Routes Middleware
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify which middleware Fortify will assign to the routes
|
||||
| that it registers with the application. If necessary, you may change
|
||||
| these middleware but typically this provided default is preferred.
|
||||
|
|
||||
*/
|
||||
|
||||
'middleware' => ['web'],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Rate Limiting
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| By default, Fortify will throttle logins to five requests per minute for
|
||||
| every email and IP address combination. However, if you would like to
|
||||
| specify a custom rate limiter to call then you may specify it here.
|
||||
|
|
||||
*/
|
||||
|
||||
'limiters' => [
|
||||
'login' => 'login',
|
||||
'two-factor' => 'two-factor',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register View Routes
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Here you may specify if the routes returning views should be disabled as
|
||||
| you may not need them when building your own application. This may be
|
||||
| especially true if you're writing a custom single-page application.
|
||||
|
|
||||
*/
|
||||
|
||||
'views' => false,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Features
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Some of the Fortify features are optional. You may disable the features
|
||||
| by removing them from this array. You're free to only remove some of
|
||||
| these features or you can even remove all of these if you need to.
|
||||
|
|
||||
*/
|
||||
|
||||
'features' => [
|
||||
Features::registration(),
|
||||
Features::resetPasswords(),
|
||||
// Features::emailVerification(),
|
||||
Features::updateProfileInformation(),
|
||||
Features::updatePasswords(),
|
||||
Features::twoFactorAuthentication([
|
||||
'confirm' => true,
|
||||
'confirmPassword' => true,
|
||||
// 'window' => 0,
|
||||
]),
|
||||
],
|
||||
|
||||
];
|
||||
@ -1,5 +1,8 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies;
|
||||
use Illuminate\Foundation\Http\Middleware\ValidateCsrfToken;
|
||||
use Laravel\Sanctum\Http\Middleware\AuthenticateSession;
|
||||
use Laravel\Sanctum\Sanctum;
|
||||
|
||||
return [
|
||||
@ -76,9 +79,9 @@
|
||||
*/
|
||||
|
||||
'middleware' => [
|
||||
'authenticate_session' => Laravel\Sanctum\Http\Middleware\AuthenticateSession::class,
|
||||
'encrypt_cookies' => Illuminate\Cookie\Middleware\EncryptCookies::class,
|
||||
'validate_csrf_token' => Illuminate\Foundation\Http\Middleware\ValidateCsrfToken::class,
|
||||
'authenticate_session' => AuthenticateSession::class,
|
||||
'encrypt_cookies' => EncryptCookies::class,
|
||||
'validate_csrf_token' => ValidateCsrfToken::class,
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->text('two_factor_secret')
|
||||
->after('password')
|
||||
->nullable();
|
||||
|
||||
$table->text('two_factor_recovery_codes')
|
||||
->after('two_factor_secret')
|
||||
->nullable();
|
||||
|
||||
$table->timestamp('two_factor_confirmed_at')
|
||||
->after('two_factor_recovery_codes')
|
||||
->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn([
|
||||
'two_factor_secret',
|
||||
'two_factor_recovery_codes',
|
||||
'two_factor_confirmed_at',
|
||||
]);
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -4,7 +4,7 @@
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
Route::get('/user', function (Request $request) {
|
||||
Route::get('/me', function (Request $request) {
|
||||
return $request->user();
|
||||
})->middleware('auth:sanctum');
|
||||
|
||||
|
||||
@ -4,5 +4,9 @@ export const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
loadComponent: () => import('./chat/chat').then(m => m.Chat)
|
||||
},
|
||||
{
|
||||
path: 'user',
|
||||
loadChildren: () => import('./auth/auth.routes').then(m => m.authRoutes)
|
||||
}
|
||||
];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user