Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
121 changes: 121 additions & 0 deletions app-modules/identity/tests/Feature/Auth/OAuthControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?php

declare(strict_types=1);

use App\Contracts\OAuthClientContract;
use Filament\Facades\Filament;
use He4rt\Identity\Auth\Actions\HandleOAuthCallbackAction;
use He4rt\Identity\Auth\DTOs\OAuthAccessDTO;
use He4rt\Identity\Auth\DTOs\OAuthStateDTO;
use He4rt\Identity\Auth\DTOs\OAuthUserDTO;
use He4rt\Identity\Auth\Enums\OAuthIntent;
use He4rt\Identity\Auth\Http\Controllers\OAuthController;
use He4rt\Identity\ExternalIdentity\Enums\IdentityProvider;
use He4rt\IntegrationGithub\OAuth\GitHubOAuthClient;
use Illuminate\Support\Facades\Auth;

function bindControllerGithubClient(): void
{
$access = new class('access-token', 'refresh-token', 3_600) extends OAuthAccessDTO
{
public static function make(array $payload): self
{
return new self('access-token', 'refresh-token', 3_600);
}
};

$user = new class($access) extends OAuthUserDTO
{
public function __construct(OAuthAccessDTO $credentials)
{
parent::__construct(
credentials: $credentials,
providerId: 'controller-github-id',
provider: IdentityProvider::GitHub,
username: 'controller-user',
name: 'Controller User',
email: 'controller@example.com',
avatarUrl: null,
);
}

public static function make(OAuthAccessDTO $credentials, array $payload): self
{
return new self($credentials);
}
};

app()->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=');
});
Comment thread
nathanmota-dev marked this conversation as resolved.
Outdated
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<script>
(() => {
const url = new URL(window.location.href);
const provider = url.searchParams.get('oauth_provider');
const supportedProviders = ['discord', 'github', 'twitch'];

if (!supportedProviders.includes(provider)) {
return;
}

try {
window.localStorage.setItem('lastAuthProvider', provider);
} catch (error) {
// Ignore browsers that block localStorage access.
}

url.searchParams.delete('oauth_provider');
window.history.replaceState({}, document.title, url);
})();
</script>
42 changes: 38 additions & 4 deletions app-modules/panel-app/resources/views/auth/login.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,29 +80,63 @@ class="h-12 w-auto text-purple-500"
<p class="mb-6 text-sm text-zinc-400">Acesse sua conta He4rt Developers</p>

{{-- OAuth --}}
<div class="grid gap-2.5">
<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="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]"
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="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"
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="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]"
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>

Expand Down
5 changes: 5 additions & 0 deletions app/Providers/Filament/AppPanelProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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'),
)
Comment thread
nathanmota-dev marked this conversation as resolved.
Outdated
->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')
Expand Down