Skip to content
Merged
9 changes: 9 additions & 0 deletions app-modules/he4rt/lang/en/auth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

return [
'continue_with' => 'Continue with :provider',
'last_access' => 'Last access',
'or' => 'or',
];
9 changes: 9 additions & 0 deletions app-modules/he4rt/lang/pt_BR/auth.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

return [
'continue_with' => 'Continuar com :provider',
'last_access' => 'Último acesso',
'or' => 'ou',
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@if(app()->isLocal())
<div class="my-6 flex items-center gap-3">
<hr class="flex-1 border-t border-zinc-800">
<span class="text-xs uppercase text-zinc-500 dark:text-zinc-400">{{ __('he4rt::auth.or') }}</span>
<hr class="flex-1 border-t border-zinc-800">
</div>

{{ $slot }}
@endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
@php
use He4rt\Identity\ExternalIdentity\Enums\CredentialsType;
use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider;

$providers = array_values(array_filter(
IdentityProvider::supportedProviders(),
fn (IdentityProvider $provider): bool => $provider->isEnabled()
&& $provider->getCredentialsType() === CredentialsType::OAuth2,
));

$brand = [
'discord' => 'bg-[#5865F2] hover:bg-[#4752C4] text-white',
'github' => 'bg-zinc-800 hover:bg-zinc-700 text-white ring-1 ring-zinc-700',
'twitch' => 'bg-[#9146FF] hover:bg-[#7B2FF0] text-white',
];
@endphp

<div
class="grid gap-2.5"
x-data="{
lastProvider: null,
init() {
try {
const provider = window.localStorage.getItem('lastAuthProvider');
this.lastProvider = @js(array_map(fn (IdentityProvider $provider): string => $provider->value, $providers)).includes(provider) ? provider : null;
} catch (error) {
this.lastProvider = null;
}
},
}"
>
@foreach ($providers as $provider)
<a
href="{{ $provider->getRedirectUri() }}"
@class([
'relative flex items-center justify-center gap-2.5 rounded-lg px-4 py-2.5 text-sm font-medium transition',
$brand[$provider->value] ?? 'bg-zinc-800 hover:bg-zinc-700 text-white ring-1 ring-zinc-700',
])
>
@svg($provider->getIcon(), 'h-5 w-5')
{{ __('he4rt::auth.continue_with', ['provider' => $provider->getLabel()]) }}
<span
x-cloak
x-show="lastProvider === @js($provider->value)"
class="absolute -top-2 right-2 z-10 rounded-full border border-zinc-600 bg-zinc-900/95 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-zinc-300 shadow-sm"
>
{{ __('he4rt::auth.last_access') }}
</span>
</a>
@endforeach
</div>
1 change: 1 addition & 0 deletions app-modules/he4rt/src/He4rtServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public function register(): void {}
public function boot(): void
{
$this->loadViewsFrom(__DIR__.'/../resources/views', 'he4rt');
$this->loadTranslationsFrom(__DIR__.'/../lang', 'he4rt');
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace He4rt\Identity\Auth\Concerns;

use Filament\Auth\Http\Responses\Contracts\LoginResponse;
use Filament\Schemas\Schema;

trait DisablesCredentialLoginOutsideLocal
{
public function form(Schema $schema): Schema
{
if (!app()->isLocal()) {
return $schema->components([]);
}

return parent::form($schema);
}

public function authenticate(): ?LoginResponse
{
abort_unless(app()->isLocal(), 404);

return parent::authenticate();
}
}
7 changes: 7 additions & 0 deletions app-modules/panel-admin/resources/views/auth/login.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<x-filament-panels::page.simple>
<x-he4rt::auth.social-login />

<x-he4rt::auth.credential-login>
{{ $this->content }}
</x-he4rt::auth.credential-login>
</x-filament-panels::page.simple>
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@

declare(strict_types=1);

namespace App\Filament\Pages;
namespace He4rt\PanelAdmin\Pages;

use He4rt\Identity\Auth\Concerns\DisablesCredentialLoginOutsideLocal;

class Login extends \Filament\Auth\Pages\Login
{
use DisablesCredentialLoginOutsideLocal;

protected string $view = 'panel-admin::auth.login';

public function mount(): void
{
parent::mount();

if (app()->environment(['local', 'staging'])) {
if (app()->isLocal()) {
$this->form->fill([
'email' => 'admin@admin.com',
'password' => 'admin',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

use He4rt\PanelAdmin\Pages\Login;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

test('admin login hides email/password outside local', function (): void {
app()->detectEnvironment(fn (): string => 'production');

$this
->get('/admin/login')
->assertOk()
->assertSee('lastAuthProvider')
->assertDontSee('data.email');
});

test('admin login shows email/password in local', function (): void {
app()->detectEnvironment(fn (): string => 'local');

$this
->get('/admin/login')
->assertOk()
->assertSee('data.email');
});

test('admin credential authenticate is unavailable outside local', function (): void {
app()->detectEnvironment(fn (): string => 'production');

(new Login)->authenticate();
})->throws(NotFoundHttpException::class);
71 changes: 4 additions & 67 deletions app-modules/panel-app/resources/views/auth/login.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,75 +80,12 @@ class="h-12 w-auto dark:brightness-0 dark:invert"
<p class="mb-6 text-sm text-zinc-400">Acesse sua conta He4rt Developers</p>

{{-- OAuth --}}
<div
class="grid gap-2.5"
x-data="{
lastProvider: null,
init() {
try {
const provider = window.localStorage.getItem('lastAuthProvider');
this.lastProvider = ['discord', 'github', 'twitch'].includes(provider) ? provider : null;
} catch (error) {
this.lastProvider = null;
}
},
}"
>
<a
href="{{ route('oauth.redirect', ['panel' => 'app', 'provider' => 'discord']) }}"
class="relative flex items-center justify-center gap-2.5 rounded-lg bg-[#5865F2] px-4 py-2.5 text-sm font-medium text-white transition hover:bg-[#4752C4]"
>
@svg ('fab-discord', 'h-5 w-5')
Continuar com Discord
<span
x-cloak
x-show="lastProvider === 'discord'"
class="absolute -top-2 right-2 z-10 rounded-full border border-zinc-600 bg-zinc-900/95 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-zinc-300 shadow-sm"
>
Último acesso
</span>
</a>

<a
href="{{ route('oauth.redirect', ['panel' => 'app', 'provider' => 'github']) }}"
class="relative flex items-center justify-center gap-2.5 rounded-lg bg-zinc-800 px-4 py-2.5 text-sm font-medium text-white ring-1 ring-zinc-700 transition hover:bg-zinc-700"
>
@svg ('fab-github', 'h-5 w-5')
Continuar com GitHub
<span
x-cloak
x-show="lastProvider === 'github'"
class="absolute -top-2 right-2 z-10 rounded-full border border-zinc-600 bg-zinc-900/95 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-zinc-300 shadow-sm"
>
Último acesso
</span>
</a>

<a
href="{{ route('oauth.redirect', ['panel' => 'app', 'provider' => 'twitch']) }}"
class="relative flex items-center justify-center gap-2.5 rounded-lg bg-[#9146FF] px-4 py-2.5 text-sm font-medium text-white transition hover:bg-[#7B2FF0]"
>
@svg ('fab-twitch', 'h-5 w-5')
Continuar com Twitch
<span
x-cloak
x-show="lastProvider === 'twitch'"
class="absolute -top-2 right-2 z-10 rounded-full border border-zinc-600 bg-zinc-900/95 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wider text-zinc-300 shadow-sm"
>
Último acesso
</span>
</a>
</div>

{{-- Divider --}}
<div class="flex items-center gap-3 my-6">
<hr class="flex-1 border-t border-zinc-800">
<span class="text-xs uppercase text-zinc-500 dark:text-zinc-400">ou</span>
<hr class="flex-1 border-t border-zinc-800">
</div>
<x-he4rt::auth.social-login />

{{-- Filament email/password form --}}
{{ $this->content }}
<x-he4rt::auth.credential-login>
{{ $this->content }}
</x-he4rt::auth.credential-login>
</div>
</div>
</div>
Expand Down
5 changes: 4 additions & 1 deletion app-modules/panel-app/src/Pages/LoginPage.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,20 @@

use Filament\Auth\Pages\Login;
use Filament\Support\Enums\Width;
use He4rt\Identity\Auth\Concerns\DisablesCredentialLoginOutsideLocal;
use Illuminate\Contracts\Support\Htmlable;

class LoginPage extends Login
{
use DisablesCredentialLoginOutsideLocal;

protected string $view = 'panel-app::auth.login';

public function mount(): void
{
parent::mount();

if (app()->environment(['local', 'staging'])) {
if (app()->isLocal()) {
$this->form->fill([
'email' => 'admin@admin.com',
'password' => 'admin',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

use He4rt\PanelApp\Pages\LoginPage;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

test('app login hides email/password outside local', function (): void {
app()->detectEnvironment(fn (): string => 'production');

$this
->get('/app/login')
->assertOk()
->assertSee('lastAuthProvider')
->assertDontSee('data.email');
});

test('app login shows email/password in local', function (): void {
app()->detectEnvironment(fn (): string => 'local');

$this
->get('/app/login')
->assertOk()
->assertSee('data.email');
});

test('app credential authenticate is unavailable outside local', function (): void {
app()->detectEnvironment(fn (): string => 'production');

(new LoginPage)->authenticate();
})->throws(NotFoundHttpException::class);
2 changes: 1 addition & 1 deletion app/Providers/Filament/AdminPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
namespace App\Providers\Filament;

use App\Enums\FilamentPanel;
use App\Filament\Pages\Login;
use App\Http\Middleware\SetApplicationLocale;
use Filament\Http\Middleware\Authenticate;
use Filament\Http\Middleware\AuthenticateSession;
Expand All @@ -15,6 +14,7 @@
use Filament\PanelProvider;
use Filament\Support\Colors\Color;
use He4rt\PanelAdmin\Pages\Dashboard;
use He4rt\PanelAdmin\Pages\Login;
use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse;
use Illuminate\Cookie\Middleware\EncryptCookies;
use Illuminate\Foundation\Http\Middleware\PreventRequestForgery;
Expand Down
10 changes: 5 additions & 5 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"php": "^8.4",
"bacon/bacon-qr-code": "^2.0.8",
"calebporzio/sushi": "^2.5.4",
"dedoc/scramble": "^0.13.45",
"dedoc/scramble": "^0.13.46",
"filament/filament": "^5.9.0",
"filament/spatie-laravel-media-library-plugin": "^5.9.0",
"guzzlehttp/guzzle": "^7.15.5",
Expand Down Expand Up @@ -40,16 +40,16 @@
"he4rt/squads": "^1.0.0",
"internachi/modular": "^3.0.2",
"laracord/framework": "dev-next#e7b64d6",
"laravel/framework": "^13.33.0",
"laravel/framework": "^13.34.0",
"laravel/head": "^0.2.2",
"laravel/nightwatch": "^1.30.2",
"laravel/sanctum": "^4.3.3",
"laravel/telescope": "^5.25.0",
"laravel/tinker": "^3.0.2",
"league/flysystem-sftp-v3": "^3.36.0",
"livewire/flux": "^2.20.0",
"livewire/flux": "^2.20.1",
"marvinlabs/laravel-discord-logger": "^1.4.4",
"matomo/device-detector": "^6.5.1",
"matomo/device-detector": "^6.5.2",
"monicahq/laravel-cloudflare": "^4.1",
"owenvoke/blade-fontawesome": "^3.3.1",
"phiki/phiki": "^2.2.1",
Expand All @@ -60,7 +60,7 @@
"spatie/laravel-medialibrary": "^11.23.8",
"spatie/laravel-permission": "^8.3.0",
"symfony/dom-crawler": "^8.1.5",
"symfony/yaml": "^8.1.6"
"symfony/yaml": "^8.1.8"
},
"require-dev": {
"driftingly/rector-laravel": "^2.6.2",
Expand Down
Loading
Loading