feat: add ability to customize session duration of clients (#1641)

This commit is contained in:
Elias Schneider
2026-08-03 23:23:29 +02:00
committed by GitHub
parent 1238bf0f37
commit 5d43c4aaeb
28 changed files with 752 additions and 77 deletions
@@ -0,0 +1,108 @@
<script lang="ts">
import * as ButtonGroup from '$lib/components/ui/button-group';
import * as Field from '$lib/components/ui/field';
import { Input } from '$lib/components/ui/input';
import * as Select from '$lib/components/ui/select';
import { m } from '$lib/paraglide/messages';
import type { FormInput } from '$lib/utils/form-util';
type DurationUnit = 'minutes' | 'hours' | 'days';
const minutesPerUnit: Record<DurationUnit, number> = {
minutes: 1,
hours: 60,
days: 24 * 60
};
const minimumMinutes = 1;
const maximumMinutes = 365 * 24 * 60;
let {
id,
label,
description,
input = $bindable()
}: {
id: string;
label: string;
description: string;
input: FormInput<number>;
} = $props();
function preferredUnit(minutes: number): DurationUnit {
if (minutes % minutesPerUnit.days === 0) return 'days';
if (minutes % minutesPerUnit.hours === 0) return 'hours';
return 'minutes';
}
function formatAmount(value: number): string {
return Number(value.toFixed(10)).toString();
}
let unit = $state<DurationUnit>(preferredUnit(input.value));
let amount = $state(formatAmount(input.value / minutesPerUnit[unit]));
function updateAmount(event: Event) {
amount = (event.currentTarget as HTMLInputElement).value;
input.value = amount === '' ? Number.NaN : Number(amount) * minutesPerUnit[unit];
}
function updateUnit(value: string | undefined) {
if (!value) return;
unit = value as DurationUnit;
if (Number.isFinite(input.value)) {
amount = formatAmount(input.value / minutesPerUnit[unit]);
}
}
function unitLabel(value: DurationUnit): string {
switch (value) {
case 'minutes':
return m.minutes();
case 'hours':
return m.hours();
case 'days':
return m.days();
}
}
</script>
<Field.Field>
<div>
<Field.Label for={id}>{label}</Field.Label>
<Field.Description>{description}</Field.Description>
</div>
<div>
<ButtonGroup.Root class="w-full">
<Input
{id}
type="number"
value={amount}
min={minimumMinutes / minutesPerUnit[unit]}
max={maximumMinutes / minutesPerUnit[unit]}
step={minimumMinutes / minutesPerUnit[unit]}
aria-invalid={!!input.error}
oninput={updateAmount}
/>
<Select.Root type="single" value={unit} onValueChange={updateUnit}>
<Select.Trigger
class="w-32"
aria-label={m.duration_unit_for({ name: label })}
aria-invalid={!!input.error}
>
{unitLabel(unit)}
</Select.Trigger>
<Select.Content>
<Select.Group>
{#each ['minutes', 'hours', 'days'] as option (option)}
<Select.Item value={option}>{unitLabel(option as DurationUnit)}</Select.Item>
{/each}
</Select.Group>
</Select.Content>
</Select.Root>
</ButtonGroup.Root>
{#if input.error}
<Field.Error>{input.error}</Field.Error>
{/if}
</div>
</Field.Field>
+7
View File
@@ -46,8 +46,15 @@ export type OidcClient = OidcClientMetaData & {
launchURL?: string;
isGroupRestricted: boolean;
pkceSupported: boolean;
accessTokenDurationMinutes: number;
refreshTokenDurationMinutes: number;
};
export type OidcClientTokenLifetimes = Pick<
OidcClient,
'accessTokenDurationMinutes' | 'refreshTokenDurationMinutes'
>;
export type OidcClientWithAllowedUserGroups = OidcClient & {
allowedUserGroups: UserGroup[];
};