Admin OIDC cleanup.

This commit is contained in:
Leo Vasanko
2026-02-17 13:36:46 +00:00
parent 8c88e6e37c
commit 7c75947662
4 changed files with 32 additions and 58 deletions
-39
View File
@@ -96,45 +96,6 @@ function copyText(value, label) {
</label> </label>
<p class="small muted">A domain ({{ rpId }} or subdomain) restricts this permission to that host. An OIDC client UUID sends it as a <em>groups</em> claim to that client.</p> <p class="small muted">A domain ({{ rpId }} or subdomain) restricts this permission to that host. An OIDC client UUID sends it as a <em>groups</em> claim to that client.</p>
</template> </template>
<template v-else-if="dialog.type==='oidc-edit'">
<template v-if="dialog.data.client_id">
<dl class="oidc-dl">
<dt>Auth Name (opt)</dt>
<dd @click="copyText('paskia', 'Authentication Name')" title="Click to copy"><output>paskia</output></dd>
<dt>Discovery URL</dt>
<dd @click="copyText(discoveryUrl, 'OpenID Connect Discovery URL')" title="Click to copy"><output>{{ discoveryUrl }}</output></dd>
<dt>Client ID</dt>
<dd @click="copyText(dialog.data.client_id, 'Client ID')" title="Click to copy"><output>{{ dialog.data.client_id }}</output></dd>
<dt>Client Secret</dt>
<dd v-if="dialog.data.client_secret" @click="copyText(dialog.data.client_secret, 'Client Secret')" title="Click to copy"><output>{{ dialog.data.client_secret }}</output></dd>
<dd v-else class="oidc-reset-row">
<button type="button" class="icon-btn" @click="$emit('resetOidcSecret', dialog.data.client_id)" title="Revoke and re-generate secret">🔄</button>
</dd>
<dt>Groups <button type="button" class="icon-btn" @click="$emit('createPermissionForClient', dialog.data.client_id)" title="Add permission scoped to this client"></button></dt>
<dd class="oidc-groups">
<template v-if="dialog.data.groups?.length">
<div v-for="group in dialog.data.groups" :key="group.uuid" class="oidc-group" @click="copyText(group.scope, 'Group Value')" :title="group.display_name">
<output>{{ group.scope }}</output>
</div>
</template>
<span v-else class="small muted">No permissions defined.</span>
</dd>
</dl>
<p v-if="dialog.data.client_secret && dialog.data.isNew" class="small"><strong> Save the secret now it cannot be retrieved later.</strong></p>
<p v-else-if="dialog.data.client_secret" class="small"><strong> Saving will prevent access with the old secret.</strong></p>
</template>
<p class="small muted">Configure these in the client application.</p>
<hr class="oidc-divider" />
<label>Client Name
<input v-model="dialog.data.name" placeholder="My Application" required />
</label>
<label>Redirect URIs
<p class="small muted">This should be provided by the client application.</p>
<textarea v-model="dialog.data.redirect_uris" placeholder="(autodiscover one on first use)" rows="2"></textarea>
</label>
</template>
<template v-else-if="dialog.type==='confirm'"> <template v-else-if="dialog.type==='confirm'">
<p>{{ dialog.data.message }}</p> <p>{{ dialog.data.message }}</p>
</template> </template>
+26 -16
View File
@@ -125,27 +125,37 @@ defineExpose({ focusFirstElement })
<p class="section-description">Configure these values in the client application.</p> <p class="section-description">Configure these values in the client application.</p>
<dl class="oidc-dl"> <dl class="oidc-dl">
<dt>Auth Name (opt)</dt> <dt>Authentication Name</dt>
<dd @click="copyText('paskia', 'Authentication Name')" title="Click to copy"><output>paskia</output></dd> <dd>
<output @click="copyText(authStore.settings.rp_name, 'Authentication Name')" title="Click to copy">{{ authStore.settings.rp_name }}</output>
<span class="small muted"> (Login With, may affect URLs optional)</span>
</dd>
<dt>Discovery URL</dt> <dt>Discovery URL</dt>
<dd @click="copyText(discoveryUrl, 'OpenID Connect Discovery URL')" title="Click to copy"><output>{{ discoveryUrl }}</output></dd> <dd><output @click="copyText(discoveryUrl, 'OpenID Connect Discovery URL')" title="Click to copy">{{ discoveryUrl }}</output></dd>
<dt>Client ID</dt> <dt>Client ID</dt>
<dd @click="copyText(clientId, 'Client ID')" title="Click to copy"><output>{{ clientId }}</output></dd> <dd><output @click="copyText(clientId, 'Client ID')" title="Click to copy">{{ clientId }}</output></dd>
<dt>Client Secret <button v-if="!clientSecret" type="button" class="icon-btn" @click="handleResetSecret" title="Revoke and re-generate secret">🔄</button></dt> <dt>Client Secret <button v-if="!clientSecret" type="button" class="icon-btn" @click="handleResetSecret" title="Revoke and re-generate secret">🔄</button></dt>
<dd v-if="clientSecret" @click="copyText(clientSecret, 'Client Secret')" title="Click to copy"><output>{{ clientSecret }}</output></dd> <dd>
<dd v-else class="muted small">(only stored in hashed form)</dd> <output v-if="clientSecret" @click="copyText(clientSecret, 'Client Secret')" title="Click to copy">{{ clientSecret }}</output>
<span v-else class="small muted">(only stored in hashed form)</span>
</dd>
<template v-if="clientGroups.length">
<dt>Groups Name</dt>
<dd>
<output @click="copyText('groups', 'Groups Name')" title="Click to copy">groups</output>
</dd>
</template>
<dt>Groups <button type="button" class="icon-btn" @click="handleCreatePermission" title="Add permission scoped to this client"></button></dt> <dt>Groups <button type="button" class="icon-btn" @click="handleCreatePermission" title="Add permission scoped to this client"></button></dt>
<dd class="oidc-groups"> <dd class="oidc-groups">
<template v-if="clientGroups.length"> <template v-if="clientGroups.length">
<div v-for="group in clientGroups" :key="group.uuid" class="oidc-group" @click="copyText(group.scope, 'Group Value')" :title="group.display_name"> <output v-for="group in clientGroups" :key="group.uuid" class="oidc-group" @click="copyText(group.scope, 'Group Value')" :title="group.display_name">{{ group.scope }}</output>
<output>{{ group.scope }}</output>
</div>
</template> </template>
<span v-else class="small muted">No permissions defined.</span> <span v-else class="small muted">(no permissions defined)</span>
</dd> </dd>
</dl> </dl>
@@ -236,17 +246,18 @@ defineExpose({ focusFirstElement })
.oidc-dl dd { .oidc-dl dd {
margin: 0; margin: 0;
cursor: pointer;
overflow: hidden; overflow: hidden;
display: flex;
align-items: baseline;
gap: 0.5em;
} }
.oidc-dl output { .oidc-dl output {
font-family: var(--font-mono, monospace); font-family: var(--font-mono, monospace);
font-size: 0.85rem;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
display: block; min-width: 0;
} }
.warning-text { .warning-text {
@@ -254,9 +265,8 @@ defineExpose({ focusFirstElement })
margin: var(--space-sm) 0 0 0; margin: var(--space-sm) 0 0 0;
} }
.oidc-groups { cursor: default; } .oidc-group { display: block; }
.oidc-group { cursor: pointer; } .oidc-group { white-space: normal; word-break: break-all; }
.oidc-group output { white-space: normal; word-break: break-all; }
.oidc-divider { .oidc-divider {
border: none; border: none;
+4
View File
@@ -279,6 +279,10 @@ button:disabled {
filter: opacity(0.6); filter: opacity(0.6);
} }
output[title="Click to copy"] {
cursor: pointer;
}
.btn-primary { .btn-primary {
background: linear-gradient(to bottom, oklch(1 0 0 / 0.15), transparent 60%) var(--color-accent); background: linear-gradient(to bottom, oklch(1 0 0 / 0.15), transparent 60%) var(--color-accent);
color: var(--color-accent-contrast); color: var(--color-accent-contrast);
+2 -3
View File
@@ -111,9 +111,8 @@ class ApiSession(msgspec.Struct, omit_defaults=True):
user_agent=useragent.compact_user_agent(s.user_agent), user_agent=useragent.compact_user_agent(s.user_agent),
last_renewed=s.expiry - expires_delta, last_renewed=s.expiry - expires_delta,
is_current=s.key == current_key, is_current=s.key == current_key,
is_current_host=not s.client_uuid and bool( is_current_host=not s.client_uuid
normalized_host and s.host and s.host == normalized_host and bool(normalized_host and s.host and s.host == normalized_host),
),
client_uuid=s.client_uuid, client_uuid=s.client_uuid,
client_name=client_name, client_name=client_name,
) )