-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#42 - feat: added passwordless login
- Loading branch information
1 parent
86b0128
commit b238238
Showing
10 changed files
with
340 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Keating\Actions; | ||
|
||
use Carbon\Carbon; | ||
use Illuminate\Support\Facades\Mail; | ||
use Illuminate\Support\Facades\URL; | ||
use Keating\Mail\LoginLink; | ||
|
||
final class SendLoginLink | ||
{ | ||
public function handle(string $email, Carbon $time): void | ||
{ | ||
Mail::to( | ||
users: $email, | ||
)->send( | ||
mailable: new LoginLink( | ||
url: URL::temporarySignedRoute( | ||
name: "passwordless.login", | ||
expiration: (int)Carbon::now()->diffInSeconds($time), | ||
parameters: [ | ||
"email" => $email, | ||
], | ||
), | ||
), | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Keating\Mail; | ||
|
||
use Illuminate\Bus\Queueable; | ||
use Illuminate\Mail\Mailable; | ||
use Illuminate\Mail\Mailables\Content; | ||
use Illuminate\Mail\Mailables\Envelope; | ||
use Illuminate\Queue\SerializesModels; | ||
|
||
final class LoginLink extends Mailable | ||
{ | ||
use Queueable; | ||
use SerializesModels; | ||
|
||
public function __construct( | ||
public readonly string $url, | ||
) {} | ||
|
||
public function envelope(): Envelope | ||
{ | ||
return new Envelope( | ||
subject: "Your Magic Link is here!", | ||
); | ||
} | ||
|
||
public function content(): Content | ||
{ | ||
return new Content( | ||
markdown: "emails.auth.login-link", | ||
with: [ | ||
"url" => $this->url, | ||
], | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace Keating\Models; | ||
|
||
use Carbon\Carbon; | ||
use Illuminate\Database\Eloquent\Concerns\HasUlids; | ||
use Illuminate\Database\Eloquent\Factories\HasFactory; | ||
use Illuminate\Database\Eloquent\Model; | ||
|
||
/** | ||
* @property string $id | ||
* @property string $email | ||
* @property string $session_id | ||
* @property bool $can_login | ||
* @property Carbon $expires_at | ||
*/ | ||
class PasswordlessAttempt extends Model | ||
{ | ||
use HasFactory; | ||
use HasUlids; | ||
|
||
protected $fillable = [ | ||
"email", | ||
"session_id", | ||
"can_login", | ||
"expires_at", | ||
]; | ||
} |
26 changes: 26 additions & 0 deletions
26
database/migrations/2024_08_28_123906_create_passwordless_attempts_table.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
use Illuminate\Database\Migrations\Migration; | ||
use Illuminate\Database\Schema\Blueprint; | ||
use Illuminate\Support\Facades\Schema; | ||
|
||
return new class() extends Migration { | ||
public function up(): void | ||
{ | ||
Schema::create("passwordless_attempts", function (Blueprint $table): void { | ||
$table->ulid("id")->primary(); | ||
$table->string("email")->unique(); | ||
$table->string("session_id"); | ||
$table->boolean("can_login")->default(false); | ||
$table->timestamp("expires_at"); | ||
$table->timestamps(); | ||
}); | ||
} | ||
|
||
public function down(): void | ||
{ | ||
Schema::dropIfExists("passwordless_attempts"); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
<script setup> | ||
import PublicLayout from '@/Layouts/PublicLayout.vue' | ||
import BackgroundGrid from '@/Components/BackgroundGrid.vue' | ||
import { Head, useForm } from '@inertiajs/inertia-vue3' | ||
import { ref } from 'vue' | ||
import axios from 'axios' | ||
import { onBeforeUnmount } from 'vue' | ||
import { Inertia } from '@inertiajs/inertia' | ||
defineProps({ | ||
university: String, | ||
universityLogo: String, | ||
}) | ||
const loginForm = useForm({ | ||
email: '', | ||
}) | ||
const interval = ref(0) | ||
function attemptLogin() { | ||
loginForm.post('/passwordless', { | ||
preserveState: true, | ||
onSuccess: () => { | ||
interval.value = setInterval(checkLogin, 2000) | ||
}, | ||
}) | ||
} | ||
async function checkLogin() { | ||
return axios.get(`/passwordless/check/${loginForm.email}`) | ||
.then(response => { | ||
if (response.status === 200) { | ||
Inertia.visit('/dashboard') | ||
} | ||
}) | ||
} | ||
onBeforeUnmount(() => { | ||
if (interval.value) { | ||
clearInterval(interval.value) | ||
} | ||
}) | ||
</script> | ||
|
||
<template> | ||
<Head title="Logowanie bez hasła" /> | ||
|
||
<PublicLayout> | ||
<div class="relative isolate bg-white pt-14"> | ||
<BackgroundGrid /> | ||
<img alt="" class="pointer-events-none absolute right-0 z-0 hidden w-1/2 opacity-10 lg:mt-16 lg:block xl:mt-10 2xl:mt-0" | ||
src="/cwup.png" | ||
> | ||
<div class="mx-auto max-w-7xl px-6 py-24 sm:py-32 lg:flex lg:items-center lg:gap-x-10 lg:px-8 lg:py-40"> | ||
<div class="mx-auto max-w-7xl text-center lg:mx-0 lg:flex-auto"> | ||
<img :alt="university" :src="universityLogo" class="mx-auto w-[360px]"> | ||
<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="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" | ||
> | ||
{{ loginForm.errors.email }} | ||
</div> | ||
<div> | ||
<label class="block text-sm font-medium leading-6 text-gray-900" for="email">Email</label> | ||
<div class="mt-2"> | ||
<input id="email" v-model="loginForm.email" autocomplete="email" class="top block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-black sm:text-sm sm:leading-6" name="email" required | ||
type="email" | ||
> | ||
</div> | ||
</div> | ||
<div> | ||
<button class="flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" | ||
type="submit" | ||
> | ||
Zaloguj się adresem e-mail | ||
</button> | ||
</div> | ||
</form> | ||
<div> | ||
<InertiaLink class="mt-3 flex w-full justify-center rounded-md bg-black px-3 py-1.5 text-sm font-semibold leading-6 text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2" | ||
href="/login" | ||
> | ||
Powrót do zwykłego logowania | ||
</InertiaLink> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</PublicLayout> | ||
</template> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<x-mail::message> | ||
# Login Link | ||
|
||
Use the link below to log into the {{ config('app.name') }} application. | ||
|
||
<x-mail::button :url="$url"> | ||
Login | ||
</x-mail::button> | ||
|
||
Thanks,<br> | ||
{{ config('app.name') }} | ||
</x-mail::message> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters