diff --git a/app/Http/Controllers/AppRegisterController.php b/app/Http/Controllers/AppRegisterController.php new file mode 100644 index 000000000..3ebf4d8f9 --- /dev/null +++ b/app/Http/Controllers/AppRegisterController.php @@ -0,0 +1,80 @@ +user()) { + if($request->user()) { + return redirect('/'); + } + return view('auth.iar'); + } + + public function store(Request $request) + { + abort_unless(config('auth.iar') == true, 404); + + $rules = [ + 'email' => 'required|email:rfc,dns,spoof,strict|unique:users,email|unique:app_registers,email', + ]; + + if ((bool) config_cache('captcha.enabled') && (bool) config_cache('captcha.active.register')) { + $rules['h-captcha-response'] = 'required|captcha'; + } + + $this->validate($request, $rules); + + $email = $request->input('email'); + $code = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT); + + $exists = AppRegister::whereEmail($email)->where('created_at', '>', now()->subHours(24))->count(); + + if($exists && $exists > 3) { + $errorParams = http_build_query([ + 'status' => 'error', + 'message' => 'Too many attempts, please try again later.' + ]); + return redirect("pixelfed://verifyEmail?{$errorParams}"); + } + + DB::beginTransaction(); + + $registration = AppRegister::create([ + 'email' => $email, + 'verify_code' => $code, + 'email_delivered_at' => now() + ]); + + try { + Mail::to($email)->send(new InAppRegisterEmailVerify($code)); + } catch (\Exception $e) { + DB::rollBack(); + $errorParams = http_build_query([ + 'status' => 'error', + 'message' => 'Failed to send verification code' + ]); + return redirect("pixelfed://verifyEmail?{$errorParams}"); + } + + DB::commit(); + + $queryParams = http_build_query([ + 'email' => $request->email, + 'expires_in' => 3600, + 'status' => 'success' + ]); + + return redirect("pixelfed://verifyEmail?{$errorParams}"); + } +} diff --git a/app/Mail/InAppRegisterEmailVerify.php b/app/Mail/InAppRegisterEmailVerify.php new file mode 100644 index 000000000..81bc99926 --- /dev/null +++ b/app/Mail/InAppRegisterEmailVerify.php @@ -0,0 +1,55 @@ +code = $code; + } + + /** + * Get the message envelope. + */ + public function envelope(): Envelope + { + return new Envelope( + subject: config('pixelfed.domain.app') . ' - Verify Your Email Address', + ); + } + + /** + * Get the message content definition. + */ + public function content(): Content + { + return new Content( + markdown: 'emails.iar.email_verify', + ); + } + + /** + * Get the attachments for the message. + * + * @return array + */ + public function attachments(): array + { + return []; + } +} diff --git a/app/Models/AppRegister.php b/app/Models/AppRegister.php new file mode 100644 index 000000000..220b4f365 --- /dev/null +++ b/app/Models/AppRegister.php @@ -0,0 +1,10 @@ + env('APP_REGISTER', false), ]; diff --git a/database/migrations/2025_01_28_102016_create_app_registers_table.php b/database/migrations/2025_01_28_102016_create_app_registers_table.php new file mode 100644 index 000000000..07121a139 --- /dev/null +++ b/database/migrations/2025_01_28_102016_create_app_registers_table.php @@ -0,0 +1,32 @@ +id(); + $table->string('email'); + $table->string('verify_code'); + $table->unique(['email', 'verify_code']); + $table->timestamp('email_delivered_at')->nullable(); + $table->timestamp('email_verified_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('app_registers'); + } +}; diff --git a/resources/views/emails/iar/email_verify.blade.php b/resources/views/emails/iar/email_verify.blade.php new file mode 100644 index 000000000..1f1a784ec --- /dev/null +++ b/resources/views/emails/iar/email_verify.blade.php @@ -0,0 +1,31 @@ +@component('mail::message') +
+ +## Verify Your Email Address + +

+ Hello, +

+ +

+ Thank you for signing up to {{config('pixelfed.domain.app')}}! +

+ +

+ To complete your registration, please enter the following verification code: +

+ +
+ {{ $code }} +
+ +

+This code will expire in 60 minutes. If you didn't request this verification, please ignore this email. +

+ +
+

If you're having trouble with the verification code, please contact our support team.

+
+ +
+@endcomponent diff --git a/resources/views/vendor/mail/html/themes/default.css b/resources/views/vendor/mail/html/themes/default.css index e5a447178..e6738c709 100644 --- a/resources/views/vendor/mail/html/themes/default.css +++ b/resources/views/vendor/mail/html/themes/default.css @@ -290,3 +290,34 @@ img { font-size: 15px; text-align: center; } + +.otcontainer { + padding: 2rem; + max-width: 600px; + margin: 0 auto; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; +} + +.otcode { + letter-spacing: 0.5rem; + font-size: 2rem; + font-weight: bold; + background-color: #f3f4f6; + padding: 1rem 2rem; + border-radius: 0.5rem; + margin: 2rem 0; + text-align: center; + color: #000000; +} + +.ottext { + color: #374151; + line-height: 1.6; + margin: 1rem 0; +} + +.otfooter { + margin-top: 2rem; + font-size: 0.875rem; + color: #6b7280; +} diff --git a/routes/web.php b/routes/web.php index b9fbad7d9..8425c6e52 100644 --- a/routes/web.php +++ b/routes/web.php @@ -139,6 +139,9 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact Route::get('discover/places/{id}/{slug}', 'PlaceController@show'); Route::get('discover/location/country/{country}', 'PlaceController@directoryCities'); + Route::get('/i/app-email-verify', 'AppRegisterController@index'); + Route::post('/i/app-email-verify', 'AppRegisterController@store'); + Route::group(['prefix' => 'i'], function () { Route::redirect('/', '/'); Route::get('compose', 'StatusController@compose')->name('compose');