diff --git a/.idea/neoban.iml b/.idea/neoban.iml
index a0063c6..cdb1fe4 100644
--- a/.idea/neoban.iml
+++ b/.idea/neoban.iml
@@ -34,7 +34,6 @@
-
@@ -139,6 +138,11 @@
+
+
+
+
+
diff --git a/.idea/php.xml b/.idea/php.xml
index 480cdc8..4bfd474 100644
--- a/.idea/php.xml
+++ b/.idea/php.xml
@@ -1,5 +1,10 @@
+
+
+
+
+
@@ -10,6 +15,9 @@
+
+
+
@@ -50,7 +58,6 @@
-
@@ -141,6 +148,11 @@
+
+
+
+
+
diff --git a/backend/.env.example b/backend/.env.example
index 60309a7..0b134d1 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -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
diff --git a/backend/app/Actions/Fortify/CreateNewUser.php b/backend/app/Actions/Fortify/CreateNewUser.php
new file mode 100644
index 0000000..ee2d712
--- /dev/null
+++ b/backend/app/Actions/Fortify/CreateNewUser.php
@@ -0,0 +1,43 @@
+ $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']),
+ ]);
+ }
+}
diff --git a/backend/app/Actions/Fortify/PasswordValidationRules.php b/backend/app/Actions/Fortify/PasswordValidationRules.php
new file mode 100644
index 0000000..3678865
--- /dev/null
+++ b/backend/app/Actions/Fortify/PasswordValidationRules.php
@@ -0,0 +1,19 @@
+|string>
+ */
+ protected function passwordRules(): array
+ {
+ return ['required', 'string', Password::default(), 'confirmed'];
+ }
+}
diff --git a/backend/app/Actions/Fortify/ResetUserPassword.php b/backend/app/Actions/Fortify/ResetUserPassword.php
new file mode 100644
index 0000000..667651f
--- /dev/null
+++ b/backend/app/Actions/Fortify/ResetUserPassword.php
@@ -0,0 +1,32 @@
+ $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();
+ }
+}
diff --git a/backend/app/Actions/Fortify/UpdateUserPassword.php b/backend/app/Actions/Fortify/UpdateUserPassword.php
new file mode 100644
index 0000000..4a0306d
--- /dev/null
+++ b/backend/app/Actions/Fortify/UpdateUserPassword.php
@@ -0,0 +1,35 @@
+ $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();
+ }
+}
diff --git a/backend/app/Actions/Fortify/UpdateUserProfileInformation.php b/backend/app/Actions/Fortify/UpdateUserProfileInformation.php
new file mode 100644
index 0000000..62f58fa
--- /dev/null
+++ b/backend/app/Actions/Fortify/UpdateUserProfileInformation.php
@@ -0,0 +1,61 @@
+ $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 $input
+ */
+ protected function updateVerifiedUser(User $user, array $input): void
+ {
+ $user->forceFill([
+ 'name' => $input['name'],
+ 'email' => $input['email'],
+ 'email_verified_at' => null,
+ ])->save();
+
+ $user->sendEmailVerificationNotification();
+ }
+}
diff --git a/backend/app/Http/Controllers/SocialMediaPostController.php b/backend/app/Http/Controllers/SocialMediaPostController.php
index b76e924..aa708a6 100644
--- a/backend/app/Http/Controllers/SocialMediaPostController.php
+++ b/backend/app/Http/Controllers/SocialMediaPostController.php
@@ -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,
diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php
index 31c83fa..4e71ebe 100644
--- a/backend/app/Models/User.php
+++ b/backend/app/Models/User.php
@@ -16,7 +16,7 @@
class User extends Authenticatable
{
/** @use HasFactory */
- use HasFactory, Notifiable, HasApiTokens;
+ use HasApiTokens, HasFactory, Notifiable;
/**
* Get the attributes that should be cast.
diff --git a/backend/app/Providers/AppServiceProvider.php b/backend/app/Providers/AppServiceProvider.php
index 452e6b6..f1525e9 100644
--- a/backend/app/Providers/AppServiceProvider.php
+++ b/backend/app/Providers/AppServiceProvider.php
@@ -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,
+ );
}
}
diff --git a/backend/app/Providers/FortifyServiceProvider.php b/backend/app/Providers/FortifyServiceProvider.php
new file mode 100644
index 0000000..004ced4
--- /dev/null
+++ b/backend/app/Providers/FortifyServiceProvider.php
@@ -0,0 +1,48 @@
+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'));
+ });
+ }
+}
diff --git a/backend/bootstrap/app.php b/backend/bootstrap/app.php
index c3928c5..ea0d646 100644
--- a/backend/bootstrap/app.php
+++ b/backend/bootstrap/app.php
@@ -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();
diff --git a/backend/bootstrap/providers.php b/backend/bootstrap/providers.php
index fc94ae6..5ffd769 100644
--- a/backend/bootstrap/providers.php
+++ b/backend/bootstrap/providers.php
@@ -1,7 +1,9 @@
=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",
diff --git a/backend/config/cors.php b/backend/config/cors.php
new file mode 100644
index 0000000..9798fb0
--- /dev/null
+++ b/backend/config/cors.php
@@ -0,0 +1,34 @@
+ ['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,
+
+];
diff --git a/backend/config/fortify.php b/backend/config/fortify.php
new file mode 100644
index 0000000..dfc5deb
--- /dev/null
+++ b/backend/config/fortify.php
@@ -0,0 +1,159 @@
+ '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,
+ ]),
+ ],
+
+];
diff --git a/backend/config/sanctum.php b/backend/config/sanctum.php
index 44527d6..cde73cf 100644
--- a/backend/config/sanctum.php
+++ b/backend/config/sanctum.php
@@ -1,5 +1,8 @@
[
- '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,
],
];
diff --git a/backend/database/migrations/2026_04_24_120752_add_two_factor_columns_to_users_table.php b/backend/database/migrations/2026_04_24_120752_add_two_factor_columns_to_users_table.php
new file mode 100644
index 0000000..45739ef
--- /dev/null
+++ b/backend/database/migrations/2026_04_24_120752_add_two_factor_columns_to_users_table.php
@@ -0,0 +1,42 @@
+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',
+ ]);
+ });
+ }
+};
diff --git a/backend/routes/api.php b/backend/routes/api.php
index 8ac7abd..1a3929f 100644
--- a/backend/routes/api.php
+++ b/backend/routes/api.php
@@ -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');
diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts
index 9a9545a..4c598f2 100644
--- a/frontend/src/app/app.routes.ts
+++ b/frontend/src/app/app.routes.ts
@@ -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)
}
];