feat(auth): Authentik OIDC sign-in + Gravatar avatars

Adds optional SSO via Authentik (or any OIDC provider) alongside the
existing password flow, and pulls profile images from the provider's
`picture` claim or Gravatar so the sharing UI stops looking anonymous.
Password login stays available as a recovery path; JIT provisioning and
admin-group mapping are env-configurable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-22 21:06:32 +02:00
parent 319be20389
commit e8e1adcf37
20 changed files with 852 additions and 60 deletions

View File

@@ -899,8 +899,12 @@ export function LeftSidebar() {
navigateToSection(`folder-${sf.id}`, { folderId: sf.id })
}
>
<Avatar name={sf.owner_username} size="xs" />
<span className="truncate" title={`${sf.name} (shared by ${sf.owner_username})`}>
<Avatar
name={sf.owner_username}
imageUrl={sf.owner_avatar_url}
size="xs"
/>
<span className="truncate" title={`${sf.name} (shared by ${sf.owner_display_name || sf.owner_username})`}>
{sf.name}
</span>
<PermissionIcon
@@ -955,8 +959,19 @@ export function LeftSidebar() {
<div className="border-t border-border p-1.5 space-y-0.5">
{/* User row */}
<div className="flex items-center gap-2 rounded px-2 py-1 text-[12px] text-text-muted">
<UserIcon className="h-3.5 w-3.5 flex-shrink-0" />
<span className="flex-1 truncate text-text">{user?.username}</span>
{user ? (
<Avatar
name={user.username}
imageUrl={user.avatar_url}
size="sm"
className="flex-shrink-0"
/>
) : (
<UserIcon className="h-3.5 w-3.5 flex-shrink-0" />
)}
<span className="flex-1 truncate text-text">
{user?.display_name || user?.username}
</span>
{isAdmin && (
<span className="rounded bg-accent/20 px-1 py-px text-[10px] leading-none text-accent flex-shrink-0">
<Shield className="inline h-2.5 w-2.5" />