Feat: make signin / signout uris render related to apptype

- Introduced `needSignInUris` and `needSignOutUris` methods in `OidcConfigResolver` to determine URI requirements based on app type.
- Updated `AuthorizationCodeClientFactory` to merge sign-in and sign-out URIs for redirect handling.
- Enhanced app creation UI to conditionally display sign-in and sign-out URI fields.
- Added authorization check for OIDC app creation using `AppPermissionEnum`.
This commit is contained in:
= 2026-06-19 09:50:41 +00:00
parent 81e9773f5b
commit 882538130b
4 changed files with 75 additions and 42 deletions

View File

@ -23,7 +23,8 @@ public function create(
): ClientCredentialsData {
$client = $this->client->createAuthorizationCodeGrantClient(
name: $data->name,
redirectUris: $data->signInUris,
// laravel-oidc-server expects redirect_uris to consist of sign_in and sign_out uris.
redirectUris: array_merge($data->signInUris, $data->signOutUris),
confidential: ApplicationTypeEnum::Web === $data->type,
);

View File

@ -21,4 +21,16 @@ public function hasClientSecret(): bool
{
return ApplicationTypeEnum::SPA !== $this->applicationType;
}
public function needSignInUris(): bool
{
return ApplicationTypeEnum::Machine !== $this->applicationType;
}
public function needSignOutUris(): bool
{
return ApplicationTypeEnum::Machine !== $this->applicationType;
}
}

View File

@ -10,8 +10,7 @@
use App\Models\{ConnectionProtocol, ConnectionStatus};
use App\Services\{ApplicationLogoUploader, ConnectedAppService};
use App\Services\Applications\OIDC\Factory\ClientCreationFactory;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\{DB, Log};
use Illuminate\Support\Str;
use Throwable;

View File

@ -1,9 +1,10 @@
<?php
use App\Concerns\HandlesOperations;
use App\Data\Application\CreatedApplicationData;
use App\Data\Application\StoreOIDCAppData;
use App\Data\ConnectedApp\ConnectedAppData;
use App\Enums\{ApplicationTypeEnum, ConnectionProtocolEnum, UserAccessTypeEnum};
use App\Enums\{ApplicationTypeEnum, ConnectionProtocolEnum, Permissions\AppPermissionEnum, UserAccessTypeEnum};
use App\Livewire\Concerns\Choices\{ChoiceField, WithSearchableChoices};
use App\Livewire\Concerns\WithRepeaterFields;
use App\Livewire\Forms\StoreOIDCAppForm;
@ -57,17 +58,14 @@ public function render()
public function save(bool $goBack = true): void
{
$this->authorize(AppPermissionEnum::Create->value);
$this->form->validate();
$this->attempt(
function () {
$data = StoreOIDCAppData::fromArray($this->applicationType, $this->form->pull());
$appdata = $this->oidcService->createApp($data);
// Show the credentials
$this->created = true;
$this->clientId = $appdata->clientCredentials->clientId;
$this->clientSecret = $appdata->clientCredentials->clientSecret;
$this->app = $appdata->application;
$this->showCredentials($appdata);
}
);
}
@ -105,6 +103,24 @@ public function clearRoles(): void
$this->clearChoice('roles');
}
#[Computed]
public function hasClientSecret(): bool
{
return $this->configResolver->for($this->applicationType)->hasClientSecret();
}
#[Computed]
public function needSignInUris(): bool
{
return $this->configResolver->for($this->applicationType)->needSignInUris();
}
#[Computed]
public function needSignOutUris(): bool
{
return $this->configResolver->for($this->applicationType)->needSignOutUris();
}
protected function choiceFields(): array
{
return [
@ -116,10 +132,12 @@ protected function choiceFields(): array
];
}
#[Computed]
public function hasClientSecret(): bool
private function showCredentials(CreatedApplicationData $appData): void
{
return $this->configResolver->for($this->applicationType)->hasClientSecret();
$this->clientId = $appData->clientCredentials->clientId;
$this->clientSecret = $appData->clientCredentials->clientSecret;
$this->app = $appData->application;
$this->created = true;
}
};
?>
@ -132,7 +150,7 @@ public function hasClientSecret(): bool
>
<div class="flex w-full justify-center items-center pt-6">
<article class="mx-auto flex items-center gap-4">
<x-mary-avatar :image="$app->logo ? asset($app->logo) : asset('images/app-icon-placeholder.png')"
<x-mary-avatar :image="$app?->logo ? asset($app->logo) : asset('images/app-icon-placeholder.png')"
alt="App logo"
class="w-14! rounded-lg! p-2"/>
<h1 class="text-2xl font-medium">{{$app->name}}</h1>
@ -186,37 +204,40 @@ class="btn-primary"
</div>
</div>
<!-- General settings end -->
@if($this->needSignInUris)
<!-- Sign-in uris start -->
<hr>
<div class="flex gap-4 flex-col w-full md:flex-row">
<article class="w-full md:w-2/5">
<h2 class="font-bold">Sign In Redirect URIs</h2>
<p class="text-sm text-gray-500 max-w-70 mt-4">
{{config('app.name')}} sends response and ID Token back to these URIs.
Allowed URIs which this app can ask to get the response back.
</p>
</article>
<div class="w-full md:w-3/5">
<p class="text-xs text-gray-500 mb-2">You can add this later.</p>
<x-shared.repeater-input model="form.signInUris" :items="$form->signInUris"/>
</div>
</div>
<!-- Sign-in uris end -->
@endif
@if($this->needSignOutUris)
<!-- Sign-out uris start -->
<hr>
<div class="flex gap-4 flex-col w-full md:flex-row">
<article class="w-full md:w-2/5">
<h2 class="font-bold">Sign out Redirect URIs</h2>
<p class="text-sm text-gray-500 max-w-70 mt-4">
{{config('app.name')}} sends back the user to these URIs after logout.
Allowed URIs which this app can redirect the user back to after logout.
</p>
</article>
<div class="w-full md:w-3/5">
<p class="text-xs text-gray-500 mb-2">You can add this later.</p>
<x-shared.repeater-input model="form.signOutUris" :items="$form->signOutUris"/>
</div>
</div>
<!-- Sign-out uris end -->
@endif
<!-- Access start -->
<hr>
<div class="flex gap-4 flex-col w-full md:flex-row">