Skip to content

Commit

Permalink
#42 - fix: code refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilpiech97 committed Aug 29, 2024
1 parent b238238 commit 4f6413e
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 95 deletions.
29 changes: 29 additions & 0 deletions app/Actions/PasswordlessCheckAndClearAttemptAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace Keating\Actions;

use Carbon\Carbon;
use Keating\Models\PasswordlessAttempt;

class PasswordlessCheckAndClearAttemptAction
{
public function handle(string $email, string $sessionId): bool
{
$passwordlessAttempt = PasswordlessAttempt::query()
->where("email", $email)
->where("can_login", true)
->where("expires_at", ">", Carbon::now())
->where("session_id", $sessionId)
->first();

if ($passwordlessAttempt === null) {
return false;
}

$passwordlessAttempt->delete();

return true;
}
}
2 changes: 1 addition & 1 deletion app/Actions/SendLoginLink.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
use Illuminate\Support\Facades\URL;
use Keating\Mail\LoginLink;

final class SendLoginLink
class SendLoginLink
{
public function handle(string $email, Carbon $time): void
{
Expand Down
81 changes: 0 additions & 81 deletions app/Http/Controllers/Public/LoginController.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,10 @@

namespace Keating\Http\Controllers\Public;

use Carbon\Carbon;
use Illuminate\Auth\AuthManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Inertia\Response;
use Keating\Actions\SendLoginLink;
use Keating\Models\PasswordlessAttempt;
use Keating\Models\User;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class LoginController
{
Expand All @@ -39,78 +32,4 @@ public function store(Request $request, AuthManager $auth): RedirectResponse
"email" => "Niepoprawne dane logowania",
]);
}

public function passwordlessCreate(): Response
{
return inertia("Public/PasswordlessLogin", [
"universityLogo" => asset("cwup-full.png"),
]);
}

public function passwordlessStore(Request $request, SendLoginLink $action): RedirectResponse
{
$time = Carbon::now()->addMinutes(5);
PasswordlessAttempt::query()
->updateOrCreate(
attributes: [
"email" => $request->email,
],
values: [
"email" => $request->email,
"session_id" => $request->session()->getId(),
"can_login" => false,
"expires_at" => $time,
],
);

$action->handle(
email: $request->email,
time: $time,
);

return back();
}

public function passwordlessCheck(Request $request): JsonResponse
{
$passwordlessAttempt = PasswordlessAttempt::query()
->where("email", $request->email)
->where("can_login", true)
->where("expires_at", ">", Carbon::now())
->where("session_id", $request->session()->getId())
->first();

if ($passwordlessAttempt === null) {
return new JsonResponse([
"can_login" => false,
], SymfonyResponse::HTTP_UNAUTHORIZED);
}

$user = User::query()
->where("email", $request->email)
->first();

Auth::login($user);

return new JsonResponse([
"can_login" => true,
], SymfonyResponse::HTTP_OK);
}

public function passwordlessLogin(Request $request, string $email): RedirectResponse
{
if (!$request->hasValidSignature()) {
abort(SymfonyResponse::HTTP_UNAUTHORIZED);
}

PasswordlessAttempt::query()
->where("email", $email)
->where("can_login", false)
->where("expires_at", ">", Carbon::now())
->update([
"can_login" => true,
]);

return redirect()->route("passwordless.create");
}
}
106 changes: 106 additions & 0 deletions app/Http/Controllers/Public/PasswordlessLoginController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
<?php

declare(strict_types=1);

namespace Keating\Http\Controllers\Public;

use Carbon\Carbon;
use Illuminate\Auth\AuthManager;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Inertia\Response;
use Keating\Actions\PasswordlessCheckAndClearAttemptAction;
use Keating\Actions\SendLoginLink;
use Keating\Models\PasswordlessAttempt;
use Keating\Models\User;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class PasswordlessLoginController
{
public function create(): Response
{
return inertia("Public/PasswordlessLogin", [
"universityLogo" => asset("cwup-full.png"),
]);
}

public function store(Request $request, SendLoginLink $action): RedirectResponse
{
$user = User::query()->where("email", $request->get("email"))->first();

if ($user === null) {
return $this->redirectToPasswordlessCreate();
}

$time = Carbon::now()->addMinutes(5);
PasswordlessAttempt::query()
->updateOrCreate(
attributes: [
"email" => $request->get("email"),
],
values: [
"email" => $request->get("email"),
"session_id" => $request->session()->getId(),
"can_login" => false,
"expires_at" => $time,
],
);

$action->handle(
email: $request->email,
time: $time,
);

return $this->redirectToPasswordlessCreate();
}

public function check(Request $request, string $email, PasswordlessCheckAndClearAttemptAction $action, AuthManager $auth): JsonResponse
{
$canLogin = $action->handle(
email: $email,
sessionId: $request->session()->getId(),
);

if (!$canLogin) {
return new JsonResponse([
"can_login" => false,
], SymfonyResponse::HTTP_UNAUTHORIZED);
}

$user = User::query()
->where("email", $email)
->first();

$auth->login($user);
$request->session()->regenerate();

return new JsonResponse([
"can_login" => true,
], SymfonyResponse::HTTP_OK);
}

public function login(Request $request, string $email): RedirectResponse
{
if (!$request->hasValidSignature()) {
abort(SymfonyResponse::HTTP_UNAUTHORIZED);
}

PasswordlessAttempt::query()
->where("email", $email)
->where("can_login", false)
->where("expires_at", ">", Carbon::now())
->update([
"can_login" => true,
]);

return redirect()->route("passwordless.create")
->with("success", "Potwierdzono logowanie.");
}

private function redirectToPasswordlessCreate(): RedirectResponse
{
return redirect()->route("passwordless.create")
->with("success", "Jeśli podany adres e-mail istnieje w naszej bazie, otrzymasz link do logowania.");
}
}
9 changes: 7 additions & 2 deletions resources/js/Pages/Public/PasswordlessLogin.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ function attemptLogin() {
loginForm.post('/passwordless', {
preserveState: true,
onSuccess: () => {
interval.value = setInterval(checkLogin, 2000)
interval.value = setInterval(checkLogin, 5000)
},
})
}
async function checkLogin() {
return axios.get(`/passwordless/check/${loginForm.email}`)
return axios.post(`/passwordless/check/${loginForm.email}`)
.then(response => {
if (response.status === 200) {
Inertia.visit('/dashboard')
Expand Down Expand Up @@ -57,6 +57,11 @@ onBeforeUnmount(() => {
<div class="sm:mx-auto sm:w-full sm:max-w-[480px]">
<div class="px-6 py-7 sm:px-12">
<form class="z-10 space-y-6" @submit.prevent="attemptLogin">
<div v-if="$page.props.flash.success"
class="text-wb-grey-80 rounded-md border border-blue-200 bg-blue-100 px-4 py-3 text-center text-sm"
>
{{ $page.props.flash.success }}
</div>
<div v-if="loginForm.errors.email"
class="bg-wb-red-10 border-wb-red-20 text-wb-grey-80 rounded-md border px-4 py-3 text-center text-sm"
>
Expand Down
23 changes: 16 additions & 7 deletions resources/views/emails/auth/login-link.blade.php
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
<x-mail::message>
# Login Link
<x-mail::layout>

Use the link below to log into the {{ config('app.name') }} application.
<x-slot:header>
<x-mail::header url="{{config('app.url')}}">
Keating
</x-mail::header>
</x-slot:header>

Zaloguj się, klikając w poniższy przycisk:

<x-mail::button :url="$url">
Login
Zaloguj
</x-mail::button>

Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
<x-slot:footer>
<x-mail::footer>
Keating &copy; {{ date('Y') }}
</x-mail::footer>
</x-slot:foot>

</x-mail::layout>
9 changes: 5 additions & 4 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
use Keating\Http\Controllers\Public\HomeController;
use Keating\Http\Controllers\Public\LoginController;
use Keating\Http\Controllers\Public\NewsController;
use Keating\Http\Controllers\Public\PasswordlessLoginController;

Route::get("/", HomeController::class)->name("main");
Route::get("/aktualnosci", [NewsController::class, "index"]);
Expand All @@ -36,10 +37,10 @@
Route::middleware("guest")->group(function (): void {
Route::get("/login", [LoginController::class, "create"])->name("login");
Route::post("/login", [LoginController::class, "store"]);
Route::get("/passwordless", [LoginController::class, "passwordlessCreate"])->name("passwordless.create");
Route::post("/passwordless", [LoginController::class, "passwordlessStore"])->name("passwordless.store");
Route::get("/passwordless/{email}", [LoginController::class, "passwordlessLogin"])->name("passwordless.login");
Route::get("/passwordless/check/{email}", [LoginController::class, "passwordlessCheck"])->name("passwordless.check");
Route::get("/passwordless", [PasswordlessLoginController::class, "create"])->name("passwordless.create");
Route::post("/passwordless", [PasswordlessLoginController::class, "store"])->name("passwordless.store");
Route::get("/passwordless/{email}", [PasswordlessLoginController::class, "login"])->name("passwordless.login");
Route::post("/passwordless/check/{email}", [PasswordlessLoginController::class, "check"])->name("passwordless.check");
});

Route::middleware("auth")->prefix("dashboard")->group(function (): void {
Expand Down
Loading

0 comments on commit 4f6413e

Please sign in to comment.