1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-10 03:14:19 +00:00

feat: allow custom client IDs (#864)

This commit is contained in:
Elias Schneider
2025-08-23 18:41:05 +02:00
committed by GitHub
parent 625f235740
commit a5efb95065
15 changed files with 151 additions and 56 deletions

View File

@@ -4,6 +4,7 @@ import type {
OidcClient,
OidcClientCreate,
OidcClientMetaData,
OidcClientUpdate,
OidcClientWithAllowedUserGroups,
OidcClientWithAllowedUserGroupsCount,
OidcDeviceCodeInfo
@@ -67,7 +68,7 @@ class OidcService extends APIService {
return (await this.api.get(`/oidc/clients/${id}/meta`)).data as OidcClientMetaData;
}
async updateClient(id: string, client: OidcClientCreate) {
async updateClient(id: string, client: OidcClientUpdate) {
return (await this.api.put(`/oidc/clients/${id}`, client)).data as OidcClient;
}

View File

@@ -37,7 +37,13 @@ export type OidcClientWithAllowedUserGroupsCount = OidcClient & {
allowedUserGroupsCount: number;
};
export type OidcClientCreate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo'>;
export type OidcClientUpdate = Omit<OidcClient, 'id' | 'logoURL' | 'hasLogo'>;
export type OidcClientCreate = OidcClientUpdate & {
id?: string;
};
export type OidcClientUpdateWithLogo = OidcClientUpdate & {
logo: File | null | undefined;
};
export type OidcClientCreateWithLogo = OidcClientCreate & {
logo: File | null | undefined;

View File

@@ -1,9 +1,7 @@
import z from 'zod/v4';
export const optionalString = z
.string()
.transform((v) => (v === '' ? undefined : v))
.optional();
export const emptyToUndefined = <T>(validation: z.ZodType<T>) =>
z.preprocess((v) => (v === '' ? undefined : v), validation);
export const optionalUrl = z
.url()

View File

@@ -5,7 +5,7 @@
import type { ApiKeyCreate } from '$lib/types/api-key.type';
import { preventDefault } from '$lib/utils/event-util';
import { createForm } from '$lib/utils/form-util';
import { optionalString } from '$lib/utils/zod-util';
import { emptyToUndefined } from '$lib/utils/zod-util';
import { z } from 'zod/v4';
let {
@@ -28,7 +28,7 @@
const formSchema = z.object({
name: z.string().min(3).max(50),
description: optionalString,
description: emptyToUndefined(z.string().optional()),
expiresAt: z.date().min(new Date(), m.expiration_date_must_be_in_the_future())
});

View File

@@ -70,7 +70,7 @@
{#if expandAddClient}
<div transition:slide>
<Card.Content>
<OIDCClientForm callback={createOIDCClient} />
<OIDCClientForm mode="create" callback={createOIDCClient} />
</Card.Content>
</div>
{/if}

View File

@@ -179,7 +179,7 @@
</Card.Root>
<Card.Root>
<Card.Content>
<OidcForm existingClient={client} callback={updateClient} />
<OidcForm mode="update" existingClient={client} callback={updateClient} />
</Card.Content>
</Card.Root>
<CollapsibleCard

View File

@@ -6,12 +6,16 @@
import { Button } from '$lib/components/ui/button';
import Label from '$lib/components/ui/label/label.svelte';
import { m } from '$lib/paraglide/messages';
import type { OidcClient, OidcClientCreateWithLogo } from '$lib/types/oidc.type';
import type {
OidcClient,
OidcClientCreateWithLogo,
OidcClientUpdateWithLogo
} from '$lib/types/oidc.type';
import { cachedOidcClientLogo } from '$lib/utils/cached-image-util';
import { preventDefault } from '$lib/utils/event-util';
import { createForm } from '$lib/utils/form-util';
import { cn } from '$lib/utils/style';
import { optionalUrl } from '$lib/utils/zod-util';
import { emptyToUndefined, optionalUrl } from '$lib/utils/zod-util';
import { LucideChevronDown } from '@lucide/svelte';
import { slide } from 'svelte/transition';
import { z } from 'zod/v4';
@@ -20,10 +24,12 @@
let {
callback,
existingClient
existingClient,
mode
}: {
existingClient?: OidcClient;
callback: (user: OidcClientCreateWithLogo) => Promise<boolean>;
callback: (client: OidcClientCreateWithLogo | OidcClientUpdateWithLogo) => Promise<boolean>;
mode: 'create' | 'update';
} = $props();
let isLoading = $state(false);
@@ -34,6 +40,7 @@
);
const client = {
id: '',
name: existingClient?.name || '',
callbackURLs: existingClient?.callbackURLs || [],
logoutCallbackURLs: existingClient?.logoutCallbackURLs || [],
@@ -47,6 +54,16 @@
};
const formSchema = z.object({
id: emptyToUndefined(
z
.string()
.min(2)
.max(128)
.regex(/^[a-zA-Z0-9_-]+$/, {
message: m.invalid_client_id()
})
.optional()
),
name: z.string().min(2).max(50),
callbackURLs: z.array(z.string().nonempty()).default([]),
logoutCallbackURLs: z.array(z.string().nonempty()),
@@ -185,7 +202,16 @@
</div>
{#if showAdvancedOptions}
<div class="mt-5 md:col-span-2" transition:slide={{ duration: 200 }}>
<div class="mt-7 flex flex-col gap-y-7 md:col-span-2" transition:slide={{ duration: 200 }}>
{#if mode == 'create'}
<FormInput
label={m.client_id()}
placeholder={m.generated()}
class="w-full md:w-1/2"
description={m.custom_client_id_description()}
bind:input={$inputs.id}
/>
{/if}
<FederatedIdentitiesInput
client={existingClient}
bind:federatedIdentities={$inputs.credentials.value.federatedIdentities}