From 349cd8f48410aab6f955616e74486f6422ab28f8 Mon Sep 17 00:00:00 2001 From: Nathan Mota Date: Tue, 14 Jul 2026 09:52:18 -0300 Subject: [PATCH 1/7] feat(auth): remember last OAuth provider --- .../Auth/Http/Controllers/OAuthController.php | 10 ++++- .../views/auth/last-provider-script.blade.php | 20 +++++++++ .../resources/views/auth/login.blade.php | 42 +++++++++++++++++-- app/Providers/Filament/AppPanelProvider.php | 5 +++ 4 files changed, 72 insertions(+), 5 deletions(-) create mode 100644 app-modules/panel-app/resources/views/auth/last-provider-script.blade.php diff --git a/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php b/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php index 378e7065..c8f2c791 100644 --- a/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php +++ b/app-modules/identity/src/Auth/Http/Controllers/OAuthController.php @@ -76,11 +76,19 @@ public function getAuthenticate(string $provider, HandleOAuthCallbackAction $act return redirect()->to($result->redirectUrl); } + $redirectUrl = $result->redirectUrl; + if ($result->intent === OAuthIntent::Login) { Auth::login($result->user); filament()->setCurrentPanel(filament()->getPanel($state->panel)); + + if ($state->panel === 'app') { + $redirectUrl = url()->query($redirectUrl, [ + 'oauth_provider' => $identityProvider->value, + ]); + } } - return redirect()->to($result->redirectUrl); + return redirect()->to($redirectUrl); } } diff --git a/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php b/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php new file mode 100644 index 00000000..31a5ace9 --- /dev/null +++ b/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php @@ -0,0 +1,20 @@ + diff --git a/app-modules/panel-app/resources/views/auth/login.blade.php b/app-modules/panel-app/resources/views/auth/login.blade.php index 7db1b862..8ecddb08 100644 --- a/app-modules/panel-app/resources/views/auth/login.blade.php +++ b/app-modules/panel-app/resources/views/auth/login.blade.php @@ -80,29 +80,63 @@ class="h-12 w-auto text-purple-500"

Acesse sua conta He4rt Developers

{{-- OAuth --}} -
+ diff --git a/app/Providers/Filament/AppPanelProvider.php b/app/Providers/Filament/AppPanelProvider.php index 10e2d7a6..6b5b4965 100644 --- a/app/Providers/Filament/AppPanelProvider.php +++ b/app/Providers/Filament/AppPanelProvider.php @@ -12,6 +12,7 @@ use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Colors\Color; +use Filament\View\PanelsRenderHook; use He4rt\PanelApp\Pages\LoginPage; use He4rt\PanelApp\Pages\ProfilePage; use He4rt\PanelApp\Pages\ThreadPage; @@ -39,6 +40,10 @@ public function panel(Panel $panel): Panel 'gray' => Color::Zinc, ]) ->viteTheme('resources/css/filament/app/theme.css') + ->renderHook( + PanelsRenderHook::BODY_END, + fn () => view('panel-app::auth.last-provider-script'), + ) ->sidebarCollapsibleOnDesktop() ->discoverResources(in: app_path('Filament/App/Resources'), for: 'App\Filament\App\Resources') ->discoverPages(in: app_path('Filament/App/Pages'), for: 'App\Filament\App\Pages') From 8412ca3bd997458a2596c4d0057878059356b2f7 Mon Sep 17 00:00:00 2001 From: Nathan Mota Date: Tue, 14 Jul 2026 09:52:28 -0300 Subject: [PATCH 2/7] test(auth): cover last OAuth provider marker --- .../Feature/Auth/OAuthControllerTest.php | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 app-modules/identity/tests/Feature/Auth/OAuthControllerTest.php diff --git a/app-modules/identity/tests/Feature/Auth/OAuthControllerTest.php b/app-modules/identity/tests/Feature/Auth/OAuthControllerTest.php new file mode 100644 index 00000000..5ba8e129 --- /dev/null +++ b/app-modules/identity/tests/Feature/Auth/OAuthControllerTest.php @@ -0,0 +1,121 @@ +instance(GitHubOAuthClient::class, new readonly class($access, $user) implements OAuthClientContract + { + public function __construct( + private OAuthAccessDTO $access, + private OAuthUserDTO $user, + ) {} + + public function redirectUrl(?OAuthStateDTO $state = null): string + { + return 'https://github.test/oauth'; + } + + public function auth(string $code): OAuthAccessDTO + { + return $this->access; + } + + public function getAuthenticatedUser(OAuthAccessDTO $credentials): OAuthUserDTO + { + return $this->user; + } + }); +} + +function callGithubCallback(OAuthStateDTO $state): string +{ + request()->merge([ + 'state' => (string) $state, + 'code' => 'auth-code', + ]); + + return resolve(OAuthController::class) + ->getAuthenticate('github', resolve(HandleOAuthCallbackAction::class)) + ->getTargetUrl(); +} + +test('successful app oauth login marks the provider in the redirect URL', function (): void { + Filament::setCurrentPanel(Filament::getPanel('app')); + bindControllerGithubClient(); + + $targetUrl = callGithubCallback(new OAuthStateDTO( + intent: OAuthIntent::Login, + provider: IdentityProvider::GitHub, + panel: 'app', + returnUrl: '/app?source=oauth', + )); + + expect($targetUrl) + ->toContain('source=oauth') + ->toContain('oauth_provider=github') + ->and(Auth::check())->toBeTrue(); +}); + +test('denied app oauth login does not mark a provider in the redirect URL', function (): void { + $state = new OAuthStateDTO( + intent: OAuthIntent::Login, + provider: IdentityProvider::GitHub, + panel: 'app', + returnUrl: '/app/login', + ); + + request()->merge([ + 'state' => (string) $state, + 'error' => 'access_denied', + ]); + + $targetUrl = resolve(OAuthController::class) + ->getAuthenticate('github', resolve(HandleOAuthCallbackAction::class)) + ->getTargetUrl(); + + expect($targetUrl) + ->toContain('/app/login') + ->not->toContain('oauth_provider='); +}); From 05f84061197ab15fcb2b7ab8bdb2007c137f26a7 Mon Sep 17 00:00:00 2001 From: Nathan Mota Date: Tue, 14 Jul 2026 10:27:35 -0300 Subject: [PATCH 3/7] refactor(auth): reuse OAuth callback URL --- .../resources/views/auth/last-provider-script.blade.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php b/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php index 31a5ace9..3ac8a7e9 100644 --- a/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php +++ b/app-modules/panel-app/resources/views/auth/last-provider-script.blade.php @@ -1,6 +1,7 @@ diff --git a/app-modules/panel-app/resources/views/auth/login.blade.php b/app-modules/panel-app/resources/views/auth/login.blade.php index 8ecddb08..66c9a95c 100644 --- a/app-modules/panel-app/resources/views/auth/login.blade.php +++ b/app-modules/panel-app/resources/views/auth/login.blade.php @@ -82,6 +82,7 @@ class="h-12 w-auto text-purple-500" {{-- OAuth --}}
+ @endif +
diff --git a/app-modules/panel-app/tests/Feature/Auth/LastAuthProviderTest.php b/app-modules/panel-app/tests/Feature/Auth/LastAuthProviderTest.php new file mode 100644 index 00000000..d5de5173 --- /dev/null +++ b/app-modules/panel-app/tests/Feature/Auth/LastAuthProviderTest.php @@ -0,0 +1,39 @@ +get(Filament::getPanel('app')->getLoginUrl()) + ->assertSuccessful() + ->assertSee("window.localStorage.getItem('lastAuthProvider')", escape: false) + ->assertDontSee('last-auth-provider', escape: false); +}); + +test('oauth callback landing loads the writer asset for a supported provider', function (): void { + $this->actingAs(User::factory()->create()); + + $this->get(url()->query(TimelinePage::getUrl(), [ + 'oauth_provider' => 'github', + ])) + ->assertSuccessful() + ->assertSee('last-auth-provider', escape: false); +}); + +test('dashboard does not load the writer asset without a supported oauth marker', function (array $query): void { + $this->actingAs(User::factory()->create()); + + $this->get(url()->query(TimelinePage::getUrl(), $query)) + ->assertSuccessful() + ->assertDontSee('last-auth-provider', escape: false); +})->with([ + 'without marker' => [[]], + 'unsupported provider' => [['oauth_provider' => 'devto']], +]); From 28b6375822c043e6e6a6ce19e2550529f994fb43 Mon Sep 17 00:00:00 2001 From: Nathan Mota Date: Sun, 19 Jul 2026 22:47:36 -0300 Subject: [PATCH 6/7] chore: remove Docker build definitions --- docker/mailpit.Dockerfile | 13 ------------- docker/postgres.Dockerfile | 14 -------------- docker/redis.Dockerfile | 13 ------------- 3 files changed, 40 deletions(-) delete mode 100644 docker/mailpit.Dockerfile delete mode 100644 docker/postgres.Dockerfile delete mode 100644 docker/redis.Dockerfile diff --git a/docker/mailpit.Dockerfile b/docker/mailpit.Dockerfile deleted file mode 100644 index b8ecb091..00000000 --- a/docker/mailpit.Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# syntax=docker/dockerfile:1.15.0 - -FROM axllent/mailpit:v1.27 - -ENV TZ=America/Sao_Paulo - -RUN set -xeu;\ - apk update;\ - apk add --no-cache tzdata nano ca-certificates;\ - ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ - echo "${TZ}" > /etc/timezone; \ - update-ca-certificates;\ - rm -rf /var/cache/apk/*; diff --git a/docker/postgres.Dockerfile b/docker/postgres.Dockerfile deleted file mode 100644 index e4f287f8..00000000 --- a/docker/postgres.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# syntax=docker/dockerfile:1.15.0 - -FROM postgres:18-alpine - -ENV TZ=Etc/UTC PGTZ=Etc/UTC - -RUN set -eux;\ - apk update;\ - apk add --no-cache tzdata nano ca-certificates;\ - apk add --no-cache postgresql-contrib postgis;\ - ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ - echo "${TZ}" > /etc/timezone;\ - update-ca-certificates;\ - rm -rf /var/cache/apk/*; diff --git a/docker/redis.Dockerfile b/docker/redis.Dockerfile deleted file mode 100644 index 69059d31..00000000 --- a/docker/redis.Dockerfile +++ /dev/null @@ -1,13 +0,0 @@ -# syntax=docker/dockerfile:1.15.0 - -FROM redis:8-alpine - -ENV TZ=America/Sao_Paulo - -RUN set -xeu; \ - apk update;\ - apk add --no-cache tzdata nano ca-certificates;\ - ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ - echo "${TZ}" > /etc/timezone;\ - update-ca-certificates;\ - rm -rf /var/cache/apk/*; From 353b40489db86fa1813d4fbb5085d897f8bfd1ff Mon Sep 17 00:00:00 2001 From: Nathan Mota Date: Sun, 19 Jul 2026 22:53:11 -0300 Subject: [PATCH 7/7] Revert "chore: remove Docker build definitions" This reverts commit 28b6375822c043e6e6a6ce19e2550529f994fb43. --- docker/mailpit.Dockerfile | 13 +++++++++++++ docker/postgres.Dockerfile | 14 ++++++++++++++ docker/redis.Dockerfile | 13 +++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 docker/mailpit.Dockerfile create mode 100644 docker/postgres.Dockerfile create mode 100644 docker/redis.Dockerfile diff --git a/docker/mailpit.Dockerfile b/docker/mailpit.Dockerfile new file mode 100644 index 00000000..b8ecb091 --- /dev/null +++ b/docker/mailpit.Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.15.0 + +FROM axllent/mailpit:v1.27 + +ENV TZ=America/Sao_Paulo + +RUN set -xeu;\ + apk update;\ + apk add --no-cache tzdata nano ca-certificates;\ + ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ + echo "${TZ}" > /etc/timezone; \ + update-ca-certificates;\ + rm -rf /var/cache/apk/*; diff --git a/docker/postgres.Dockerfile b/docker/postgres.Dockerfile new file mode 100644 index 00000000..e4f287f8 --- /dev/null +++ b/docker/postgres.Dockerfile @@ -0,0 +1,14 @@ +# syntax=docker/dockerfile:1.15.0 + +FROM postgres:18-alpine + +ENV TZ=Etc/UTC PGTZ=Etc/UTC + +RUN set -eux;\ + apk update;\ + apk add --no-cache tzdata nano ca-certificates;\ + apk add --no-cache postgresql-contrib postgis;\ + ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ + echo "${TZ}" > /etc/timezone;\ + update-ca-certificates;\ + rm -rf /var/cache/apk/*; diff --git a/docker/redis.Dockerfile b/docker/redis.Dockerfile new file mode 100644 index 00000000..69059d31 --- /dev/null +++ b/docker/redis.Dockerfile @@ -0,0 +1,13 @@ +# syntax=docker/dockerfile:1.15.0 + +FROM redis:8-alpine + +ENV TZ=America/Sao_Paulo + +RUN set -xeu; \ + apk update;\ + apk add --no-cache tzdata nano ca-certificates;\ + ln -snf /usr/share/zoneinfo/"${TZ}" /etc/localtime;\ + echo "${TZ}" > /etc/timezone;\ + update-ca-certificates;\ + rm -rf /var/cache/apk/*;