Admin OIDC cleanup.
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user