mirror of
https://github.com/coollabsio/coolify.git
synced 2026-08-24 02:24:11 -05:00
feat(auth): improve OAuth buttons and OIDC settings layout
Add provider icons and full-width centered login buttons, organize OIDC fields, and remove hardcoded user IDs from the seeder.
This commit is contained in:
@@ -15,12 +15,10 @@ class UserSeeder extends Seeder
|
||||
'email' => 'test@example.com',
|
||||
]);
|
||||
User::factory()->create([
|
||||
'id' => 1,
|
||||
'name' => 'Normal User (but in root team)',
|
||||
'email' => 'test2@example.com',
|
||||
]);
|
||||
User::factory()->create([
|
||||
'id' => 2,
|
||||
'name' => 'Normal User (not in root team)',
|
||||
'email' => 'test3@example.com',
|
||||
]);
|
||||
|
||||
@@ -80,10 +80,14 @@
|
||||
|
||||
@if ($enabled_oauth_providers->isNotEmpty())
|
||||
<div class="auth-divider"><span>Or continue with</span></div>
|
||||
<div class="grid gap-2 sm:grid-cols-2">
|
||||
<div class="flex flex-col gap-2">
|
||||
@foreach ($enabled_oauth_providers as $provider_setting)
|
||||
<x-forms.button class="w-full justify-center" type="button"
|
||||
onclick="document.location.href='/auth/{{ $provider_setting->provider }}/redirect'">
|
||||
@if ($provider_setting->provider !== 'oidc')
|
||||
<img class="size-5 shrink-0 dark:invert"
|
||||
src="{{ asset('svgs/'.$provider_setting->provider.'.svg') }}" alt="" aria-hidden="true">
|
||||
@endif
|
||||
{{ $provider_setting->loginLabel() }}
|
||||
</x-forms.button>
|
||||
@endforeach
|
||||
|
||||
@@ -55,14 +55,39 @@
|
||||
</x-slot:actions>
|
||||
|
||||
<div class="grid gap-4 lg:grid-cols-2">
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.redirect_uri"
|
||||
placeholder="{{ route('auth.callback', $provider) }}" label="Redirect URI" />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_id" label="Client ID" required />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_secret" type="password"
|
||||
label="Client secret" autocomplete="new-password" required />
|
||||
@if ($provider === 'oidc')
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.redirect_uri"
|
||||
placeholder="{{ route('auth.callback', $provider) }}" label="Redirect URI" />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.base_url" label="Issuer URL" required
|
||||
helper="OpenID Provider issuer URL, for example https://example.okta.com. Coolify uses it to discover the authorization, token, userinfo, and JWKS endpoints." />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_id" label="Client ID" required />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_secret" type="password"
|
||||
label="Client secret" autocomplete="new-password" required />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.scopes" label="Scopes"
|
||||
helper="Must include openid. Common scopes are openid email profile groups." />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.clock_skew_seconds" type="number"
|
||||
label="Clock skew (seconds)" />
|
||||
<div class="lg:col-span-2">
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.custom_label" label="Login button label"
|
||||
placeholder="Login with SSO" />
|
||||
</div>
|
||||
@else
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.redirect_uri"
|
||||
placeholder="{{ route('auth.callback', $provider) }}" label="Redirect URI" />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_id" label="Client ID" required />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.client_secret" type="password"
|
||||
label="Client secret" autocomplete="new-password" required />
|
||||
@endif
|
||||
|
||||
@if ($provider === 'azure')
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
@@ -82,20 +107,6 @@
|
||||
:required="in_array($provider, ['authentik', 'clerk'], true)" />
|
||||
@endif
|
||||
|
||||
@if ($provider === 'oidc')
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.base_url" label="Issuer URL" required
|
||||
helper="OpenID Provider issuer URL, for example https://example.okta.com. Coolify uses it to discover the authorization, token, userinfo, and JWKS endpoints." />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.custom_label" label="Login button label"
|
||||
placeholder="Login with SSO" />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.scopes" label="Scopes"
|
||||
helper="Must include openid. Common scopes are openid email profile groups." />
|
||||
<x-forms.input canGate="update" :canResource="$settings"
|
||||
id="oauth_settings_map.{{ $provider }}.clock_skew_seconds" type="number"
|
||||
label="Clock skew (seconds)" />
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="mt-4 grid gap-3 lg:grid-cols-2">
|
||||
|
||||
@@ -37,6 +37,28 @@ test('auth pages use the Coollabs purple background glow', function () {
|
||||
->not->toMatch('/\.auth-shell\s*\{[^}]*color-mix\(in oklab, var\(--color-accent\) 9%, transparent\)/s');
|
||||
});
|
||||
|
||||
test('external login providers are centered and full width', function () {
|
||||
$login = file_get_contents(resource_path('views/auth/login.blade.php'));
|
||||
|
||||
expect($login)
|
||||
->toContain('class="flex flex-col gap-2"')
|
||||
->toContain('class="w-full justify-center"')
|
||||
->not->toContain('sm:w-[calc(50%-0.25rem)]');
|
||||
});
|
||||
|
||||
test('external login providers display their icons except oidc', function () {
|
||||
$login = file_get_contents(resource_path('views/auth/login.blade.php'));
|
||||
|
||||
expect($login)
|
||||
->toContain("@if (\$provider_setting->provider !== 'oidc')")
|
||||
->toContain("asset('svgs/'.\$provider_setting->provider.'.svg')")
|
||||
->toContain('class="size-5 shrink-0 dark:invert"');
|
||||
|
||||
foreach (['authentik', 'azure', 'bitbucket', 'clerk', 'discord', 'github', 'gitlab', 'google', 'infomaniak', 'zitadel'] as $provider) {
|
||||
expect(public_path("svgs/{$provider}.svg"))->toBeFile();
|
||||
}
|
||||
});
|
||||
|
||||
test('error pages use the Coollabs purple background glow', function () {
|
||||
$styles = file_get_contents(resource_path('css/app.css'));
|
||||
|
||||
|
||||
@@ -94,6 +94,27 @@ it('shows oidc fields with a naked okta issuer url example', function () {
|
||||
->assertDontSee('/oauth2/default', false);
|
||||
});
|
||||
|
||||
it('groups oidc fields in the expected desktop order', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/settings-oauth.blade.php'));
|
||||
$fields = [
|
||||
'redirect_uri',
|
||||
'base_url',
|
||||
'client_id',
|
||||
'client_secret',
|
||||
'scopes',
|
||||
'clock_skew_seconds',
|
||||
'custom_label',
|
||||
];
|
||||
$positions = array_map(
|
||||
fn (string $field): int|false => strpos($view, "id=\"oauth_settings_map.{{ \$provider }}.$field\""),
|
||||
$fields,
|
||||
);
|
||||
|
||||
expect($positions)->not->toContain(false)
|
||||
->and($positions)->toBe(collect($positions)->sort()->values()->all())
|
||||
->and($view)->toContain('<div class="lg:col-span-2">');
|
||||
});
|
||||
|
||||
it('shows provider enable controls as settings section actions', function () {
|
||||
actingAsInstanceAdmin();
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use App\Models\User;
|
||||
use Database\Seeders\UserSeeder;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
it('leaves the user id sequence ready for new development users', function () {
|
||||
$this->seed(UserSeeder::class);
|
||||
|
||||
$user = User::factory()->create();
|
||||
|
||||
expect(User::query()->orderBy('id')->pluck('id')->all())->toBe([0, 1, 2, 3])
|
||||
->and($user->id)->toBe(3);
|
||||
});
|
||||
Reference in New Issue
Block a user