diff --git a/.env.example b/.env.example index 35db1ddf..5ef3615e 100644 --- a/.env.example +++ b/.env.example @@ -56,6 +56,10 @@ MAIL_PASSWORD=null MAIL_FROM_ADDRESS="hello@example.com" MAIL_FROM_NAME="${APP_NAME}" +WORKOS_CLIENT_ID= +WORKOS_API_KEY= +WORKOS_REDIRECT_URL="http://localhost:8000/authenticate" + AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 9f2ccc07..a928624e 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -5,10 +5,12 @@ on: branches: - develop - main + - workos pull_request: branches: - develop - main + - workos permissions: contents: write diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 911ac4e6..5ad2320d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -5,10 +5,12 @@ on: branches: - develop - main + - workos pull_request: branches: - develop - main + - workos jobs: ci: diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php deleted file mode 100644 index dbb93653..00000000 --- a/app/Http/Controllers/Auth/AuthenticatedSessionController.php +++ /dev/null @@ -1,51 +0,0 @@ - Route::has('password.request'), - 'status' => $request->session()->get('status'), - ]); - } - - /** - * Handle an incoming authentication request. - */ - public function store(LoginRequest $request): RedirectResponse - { - $request->authenticate(); - - $request->session()->regenerate(); - - return redirect()->intended(route('dashboard', absolute: false)); - } - - /** - * Destroy an authenticated session. - */ - public function destroy(Request $request): RedirectResponse - { - Auth::guard('web')->logout(); - - $request->session()->invalidate(); - $request->session()->regenerateToken(); - - return redirect('/'); - } -} diff --git a/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/app/Http/Controllers/Auth/ConfirmablePasswordController.php deleted file mode 100644 index fb7d8e0d..00000000 --- a/app/Http/Controllers/Auth/ConfirmablePasswordController.php +++ /dev/null @@ -1,41 +0,0 @@ -validate([ - 'email' => $request->user()->email, - 'password' => $request->password, - ])) { - throw ValidationException::withMessages([ - 'password' => __('auth.password'), - ]); - } - - $request->session()->put('auth.password_confirmed_at', time()); - - return redirect()->intended(route('dashboard', absolute: false)); - } -} diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php deleted file mode 100644 index f64fa9ba..00000000 --- a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php +++ /dev/null @@ -1,24 +0,0 @@ -user()->hasVerifiedEmail()) { - return redirect()->intended(route('dashboard', absolute: false)); - } - - $request->user()->sendEmailVerificationNotification(); - - return back()->with('status', 'verification-link-sent'); - } -} diff --git a/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/app/Http/Controllers/Auth/EmailVerificationPromptController.php deleted file mode 100644 index bf57a208..00000000 --- a/app/Http/Controllers/Auth/EmailVerificationPromptController.php +++ /dev/null @@ -1,22 +0,0 @@ -user()->hasVerifiedEmail() - ? redirect()->intended(route('dashboard', absolute: false)) - : Inertia::render('auth/VerifyEmail', ['status' => $request->session()->get('status')]); - } -} diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php deleted file mode 100644 index 56ae905a..00000000 --- a/app/Http/Controllers/Auth/NewPasswordController.php +++ /dev/null @@ -1,69 +0,0 @@ - $request->email, - 'token' => $request->route('token'), - ]); - } - - /** - * Handle an incoming new password request. - * - * @throws \Illuminate\Validation\ValidationException - */ - public function store(Request $request): RedirectResponse - { - $request->validate([ - 'token' => 'required', - 'email' => 'required|email', - 'password' => ['required', 'confirmed', Rules\Password::defaults()], - ]); - - // Here we will attempt to reset the user's password. If it is successful we - // will update the password on an actual user model and persist it to the - // database. Otherwise we will parse the error and return the response. - $status = Password::reset( - $request->only('email', 'password', 'password_confirmation', 'token'), - function ($user) use ($request) { - $user->forceFill([ - 'password' => Hash::make($request->password), - 'remember_token' => Str::random(60), - ])->save(); - - event(new PasswordReset($user)); - } - ); - - // If the password was successfully reset, we will redirect the user back to - // the application's home authenticated view. If there is an error we can - // redirect them back to where they came from with their error message. - if ($status == Password::PasswordReset) { - return to_route('login')->with('status', __($status)); - } - - throw ValidationException::withMessages([ - 'email' => [__($status)], - ]); - } -} diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php deleted file mode 100644 index a2b6e381..00000000 --- a/app/Http/Controllers/Auth/PasswordResetLinkController.php +++ /dev/null @@ -1,41 +0,0 @@ - $request->session()->get('status'), - ]); - } - - /** - * Handle an incoming password reset link request. - * - * @throws \Illuminate\Validation\ValidationException - */ - public function store(Request $request): RedirectResponse - { - $request->validate([ - 'email' => 'required|email', - ]); - - Password::sendResetLink( - $request->only('email') - ); - - return back()->with('status', __('A reset link will be sent if the account exists.')); - } -} diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php deleted file mode 100644 index c7138cac..00000000 --- a/app/Http/Controllers/Auth/RegisteredUserController.php +++ /dev/null @@ -1,51 +0,0 @@ -validate([ - 'name' => 'required|string|max:255', - 'email' => 'required|string|lowercase|email|max:255|unique:'.User::class, - 'password' => ['required', 'confirmed', Rules\Password::defaults()], - ]); - - $user = User::create([ - 'name' => $request->name, - 'email' => $request->email, - 'password' => Hash::make($request->password), - ]); - - event(new Registered($user)); - - Auth::login($user); - - return to_route('dashboard'); - } -} diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php deleted file mode 100644 index 2477faa5..00000000 --- a/app/Http/Controllers/Auth/VerifyEmailController.php +++ /dev/null @@ -1,29 +0,0 @@ -user()->hasVerifiedEmail()) { - return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); - } - - if ($request->user()->markEmailAsVerified()) { - /** @var \Illuminate\Contracts\Auth\MustVerifyEmail $user */ - $user = $request->user(); - event(new Verified($user)); - } - - return redirect()->intended(route('dashboard', absolute: false).'?verified=1'); - } -} diff --git a/app/Http/Controllers/Settings/PasswordController.php b/app/Http/Controllers/Settings/PasswordController.php deleted file mode 100644 index 33d5a605..00000000 --- a/app/Http/Controllers/Settings/PasswordController.php +++ /dev/null @@ -1,39 +0,0 @@ -validate([ - 'current_password' => ['required', 'current_password'], - 'password' => ['required', Password::defaults(), 'confirmed'], - ]); - - $request->user()->update([ - 'password' => Hash::make($validated['password']), - ]); - - return back(); - } -} diff --git a/app/Http/Controllers/Settings/ProfileController.php b/app/Http/Controllers/Settings/ProfileController.php index 10f3d224..6ed259c2 100644 --- a/app/Http/Controllers/Settings/ProfileController.php +++ b/app/Http/Controllers/Settings/ProfileController.php @@ -3,13 +3,12 @@ namespace App\Http\Controllers\Settings; use App\Http\Controllers\Controller; -use App\Http\Requests\Settings\ProfileUpdateRequest; -use Illuminate\Contracts\Auth\MustVerifyEmail; +use App\Models\User; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; -use Illuminate\Support\Facades\Auth; use Inertia\Inertia; use Inertia\Response; +use Laravel\WorkOS\Http\Requests\AuthKitAccountDeletionRequest; class ProfileController extends Controller { @@ -19,45 +18,31 @@ class ProfileController extends Controller public function edit(Request $request): Response { return Inertia::render('settings/Profile', [ - 'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail, 'status' => $request->session()->get('status'), ]); } /** - * Update the user's profile information. + * Update the user's profile settings. */ - public function update(ProfileUpdateRequest $request): RedirectResponse + public function update(Request $request): RedirectResponse { - $request->user()->fill($request->validated()); - - if ($request->user()->isDirty('email')) { - $request->user()->email_verified_at = null; - } + $request->validate([ + 'name' => ['required', 'string', 'max:255'], + ]); - $request->user()->save(); + $request->user()->update(['name' => $request->name]); return to_route('profile.edit'); } /** - * Delete the user's profile. + * Delete the user's account. */ - public function destroy(Request $request): RedirectResponse + public function destroy(AuthKitAccountDeletionRequest $request): RedirectResponse { - $request->validate([ - 'password' => ['required', 'current_password'], - ]); - - $user = $request->user(); - - Auth::logout(); - - $user->delete(); - - $request->session()->invalidate(); - $request->session()->regenerateToken(); - - return redirect('/'); + return $request->delete( + using: fn (User $user) => $user->delete() + ); } } diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php deleted file mode 100644 index 25746424..00000000 --- a/app/Http/Requests/Auth/LoginRequest.php +++ /dev/null @@ -1,85 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'email' => ['required', 'string', 'email'], - 'password' => ['required', 'string'], - ]; - } - - /** - * Attempt to authenticate the request's credentials. - * - * @throws \Illuminate\Validation\ValidationException - */ - public function authenticate(): void - { - $this->ensureIsNotRateLimited(); - - if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) { - RateLimiter::hit($this->throttleKey()); - - throw ValidationException::withMessages([ - 'email' => trans('auth.failed'), - ]); - } - - RateLimiter::clear($this->throttleKey()); - } - - /** - * Ensure the login request is not rate limited. - * - * @throws \Illuminate\Validation\ValidationException - */ - public function ensureIsNotRateLimited(): void - { - if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) { - return; - } - - event(new Lockout($this)); - - $seconds = RateLimiter::availableIn($this->throttleKey()); - - throw ValidationException::withMessages([ - 'email' => trans('auth.throttle', [ - 'seconds' => $seconds, - 'minutes' => ceil($seconds / 60), - ]), - ]); - } - - /** - * Get the rate limiting throttle key for the request. - */ - public function throttleKey(): string - { - return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip()); - } -} diff --git a/app/Http/Requests/Settings/ProfileUpdateRequest.php b/app/Http/Requests/Settings/ProfileUpdateRequest.php deleted file mode 100644 index c294aab2..00000000 --- a/app/Http/Requests/Settings/ProfileUpdateRequest.php +++ /dev/null @@ -1,30 +0,0 @@ -|string> - */ - public function rules(): array - { - return [ - 'name' => ['required', 'string', 'max:255'], - 'email' => [ - 'required', - 'string', - 'lowercase', - 'email', - 'max:255', - Rule::unique(User::class)->ignore($this->user()->id), - ], - ]; - } -} diff --git a/app/Models/User.php b/app/Models/User.php index 749c7b77..bff72d3d 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -20,7 +20,8 @@ class User extends Authenticatable protected $fillable = [ 'name', 'email', - 'password', + 'workos_id', + 'avatar', ]; /** @@ -29,7 +30,7 @@ class User extends Authenticatable * @var list */ protected $hidden = [ - 'password', + 'workos_id', 'remember_token', ]; diff --git a/composer.json b/composer.json index ab0ae90e..f13f5927 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "inertiajs/inertia-laravel": "^2.0", "laravel/framework": "^12.0", "laravel/tinker": "^2.10.1", + "laravel/workos": "^0.1.0", "tightenco/ziggy": "^2.4" }, "require-dev": { diff --git a/config/services.php b/config/services.php index 27a36175..cdb08d0a 100644 --- a/config/services.php +++ b/config/services.php @@ -35,4 +35,10 @@ ], ], + 'workos' => [ + 'client_id' => env('WORKOS_CLIENT_ID'), + 'secret' => env('WORKOS_API_KEY'), + 'redirect_url' => env('WORKOS_REDIRECT_URL'), + ], + ]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 584104c9..bd8b2c3d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -3,7 +3,6 @@ namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; -use Illuminate\Support\Facades\Hash; use Illuminate\Support\Str; /** @@ -11,11 +10,6 @@ */ class UserFactory extends Factory { - /** - * The current password being used by the factory. - */ - protected static ?string $password; - /** * Define the model's default state. * @@ -27,8 +21,9 @@ public function definition(): array 'name' => fake()->name(), 'email' => fake()->unique()->safeEmail(), 'email_verified_at' => now(), - 'password' => static::$password ??= Hash::make('password'), + 'workos_id' => 'fake-'.Str::random(10), 'remember_token' => Str::random(10), + 'avatar' => '', ]; } diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 05fb5d9e..cd6072cb 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -16,17 +16,12 @@ public function up(): void $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); - $table->string('password'); + $table->string('workos_id')->unique(); $table->rememberToken(); + $table->text('avatar'); $table->timestamps(); }); - Schema::create('password_reset_tokens', function (Blueprint $table) { - $table->string('email')->primary(); - $table->string('token'); - $table->timestamp('created_at')->nullable(); - }); - Schema::create('sessions', function (Blueprint $table) { $table->string('id')->primary(); $table->foreignId('user_id')->nullable()->index(); @@ -43,7 +38,6 @@ public function up(): void public function down(): void { Schema::dropIfExists('users'); - Schema::dropIfExists('password_reset_tokens'); Schema::dropIfExists('sessions'); } }; diff --git a/resources/js/components/DeleteUser.vue b/resources/js/components/DeleteUser.vue index 550610cb..2f38a6f8 100644 --- a/resources/js/components/DeleteUser.vue +++ b/resources/js/components/DeleteUser.vue @@ -1,10 +1,7 @@ - - diff --git a/resources/js/layouts/auth/AuthCardLayout.vue b/resources/js/layouts/auth/AuthCardLayout.vue deleted file mode 100644 index 98668181..00000000 --- a/resources/js/layouts/auth/AuthCardLayout.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/resources/js/layouts/auth/AuthSimpleLayout.vue b/resources/js/layouts/auth/AuthSimpleLayout.vue deleted file mode 100644 index a208182f..00000000 --- a/resources/js/layouts/auth/AuthSimpleLayout.vue +++ /dev/null @@ -1,31 +0,0 @@ - - - diff --git a/resources/js/layouts/auth/AuthSplitLayout.vue b/resources/js/layouts/auth/AuthSplitLayout.vue deleted file mode 100644 index 8bd6d78e..00000000 --- a/resources/js/layouts/auth/AuthSplitLayout.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - diff --git a/resources/js/layouts/settings/Layout.vue b/resources/js/layouts/settings/Layout.vue index 0b321e93..f6f98be9 100644 --- a/resources/js/layouts/settings/Layout.vue +++ b/resources/js/layouts/settings/Layout.vue @@ -10,10 +10,6 @@ const sidebarNavItems: NavItem[] = [ title: 'Profile', href: '/settings/profile', }, - { - title: 'Password', - href: '/settings/password', - }, { title: 'Appearance', href: '/settings/appearance', diff --git a/resources/js/pages/Welcome.vue b/resources/js/pages/Welcome.vue index 3560aa58..401c8343 100644 --- a/resources/js/pages/Welcome.vue +++ b/resources/js/pages/Welcome.vue @@ -24,12 +24,6 @@ import { Head, Link } from '@inertiajs/vue3'; > Log in - - Register - diff --git a/resources/js/pages/auth/ConfirmPassword.vue b/resources/js/pages/auth/ConfirmPassword.vue deleted file mode 100644 index 6d538023..00000000 --- a/resources/js/pages/auth/ConfirmPassword.vue +++ /dev/null @@ -1,53 +0,0 @@ - - - diff --git a/resources/js/pages/auth/ForgotPassword.vue b/resources/js/pages/auth/ForgotPassword.vue deleted file mode 100644 index e992a14f..00000000 --- a/resources/js/pages/auth/ForgotPassword.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - diff --git a/resources/js/pages/auth/Login.vue b/resources/js/pages/auth/Login.vue deleted file mode 100644 index 02aff22b..00000000 --- a/resources/js/pages/auth/Login.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - diff --git a/resources/js/pages/auth/Register.vue b/resources/js/pages/auth/Register.vue deleted file mode 100644 index c0a757d0..00000000 --- a/resources/js/pages/auth/Register.vue +++ /dev/null @@ -1,83 +0,0 @@ - - - diff --git a/resources/js/pages/auth/ResetPassword.vue b/resources/js/pages/auth/ResetPassword.vue deleted file mode 100644 index 1bca6974..00000000 --- a/resources/js/pages/auth/ResetPassword.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - diff --git a/resources/js/pages/auth/VerifyEmail.vue b/resources/js/pages/auth/VerifyEmail.vue deleted file mode 100644 index dad75f73..00000000 --- a/resources/js/pages/auth/VerifyEmail.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - diff --git a/resources/js/pages/settings/Password.vue b/resources/js/pages/settings/Password.vue deleted file mode 100644 index f4c35c11..00000000 --- a/resources/js/pages/settings/Password.vue +++ /dev/null @@ -1,119 +0,0 @@ - - - diff --git a/resources/js/pages/settings/Profile.vue b/resources/js/pages/settings/Profile.vue index a1028f6f..c4758cdc 100644 --- a/resources/js/pages/settings/Profile.vue +++ b/resources/js/pages/settings/Profile.vue @@ -1,5 +1,5 @@