From aaa5f4cb2f03048ff2a4ead7cff8926dac6a1bfd Mon Sep 17 00:00:00 2001 From: Stavros Date: Wed, 24 Jun 2026 12:27:30 +0300 Subject: [PATCH 1/2] feat: show provider badge in quick actions --- frontend/src/components/icons/local-auth.tsx | 22 ++++++ .../quick-actions/quick-actions.tsx | 72 +++++++++++++++++-- frontend/src/lib/i18n/locales/en.json | 5 +- 3 files changed, 92 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/icons/local-auth.tsx diff --git a/frontend/src/components/icons/local-auth.tsx b/frontend/src/components/icons/local-auth.tsx new file mode 100644 index 00000000..d17391bd --- /dev/null +++ b/frontend/src/components/icons/local-auth.tsx @@ -0,0 +1,22 @@ +import type { SVGProps } from "react"; + +export function LocalAuthIcon(props: SVGProps) { + return ( + + + + ); +} diff --git a/frontend/src/components/quick-actions/quick-actions.tsx b/frontend/src/components/quick-actions/quick-actions.tsx index 1287ec6b..ae3d4828 100644 --- a/frontend/src/components/quick-actions/quick-actions.tsx +++ b/frontend/src/components/quick-actions/quick-actions.tsx @@ -37,6 +37,13 @@ import { useMutation } from "@tanstack/react-query"; import axios from "axios"; import { toast } from "sonner"; import { useEffect } from "react"; +import { GoogleIcon } from "../icons/google"; +import { GithubIcon } from "../icons/github"; +import { TailscaleIcon } from "../icons/tailscale"; +import { MicrosoftIcon } from "../icons/microsoft"; +import { PocketIDIcon } from "../icons/pocket-id"; +import { LocalAuthIcon } from "../icons/local-auth"; +import { OAuthIcon } from "../icons/oauth"; function Avatar({ initial }: { initial: string }) { return ( @@ -49,8 +56,18 @@ function Avatar({ initial }: { initial: string }) { ); } +const iconStyles = "size-4"; + +const iconMap: Record = { + google: , + github: , + tailscale: , + microsoft: , + pocketid: , +}; + export const QuickActions = () => { - const { auth } = useUserContext(); + const { auth, oauth, tailscale } = useUserContext(); const { theme, setTheme } = useTheme(); const { t } = useTranslation(); const { search } = useLocation(); @@ -64,6 +81,41 @@ export const QuickActions = () => { const screenParams = useScreenParams(searchParams); const compiledParams = recompileScreenParams(screenParams); + const providerDetails = ((): + | { name: string; icon: React.ReactNode } + | undefined => { + if (!auth.authenticated) { + return undefined; + } + + if (auth.providerId === "local" || auth.providerId === "ldap") { + return { + name: t( + auth.providerId === "ldap" + ? "quickActionsProviderLDAP" + : "quickActionsProviderLocal", + ), + icon: , + }; + } + + if (oauth.active) { + return { + name: t("quickActionsProviderOAuth", { provider: oauth.displayName }), + icon: iconMap[auth.providerId] || , + }; + } + + if (auth.providerId === "tailscale") { + return { + name: `Tailscale (${tailscale.nodeName})`, + icon: , + }; + } + + return undefined; + })(); + const logoutMutation = useMutation({ mutationFn: () => axios.post("/api/user/logout"), mutationKey: ["logout"], @@ -134,11 +186,19 @@ export const QuickActions = () => {
{initial}
-
- - {auth.name} - - +
+
+ + {auth.name} + + {providerDetails && ( + + {providerDetails.icon} + {providerDetails.name} + + )} +
+ {auth.email}
diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index dbe05c1a..9eb3dc70 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -99,5 +99,8 @@ "quickActionsThemeDark": "Dark", "quickActionsThemeSystem": "System", "quickActionsLogout": "Logout", - "quickActionsTitle": "Quick Actions" + "quickActionsTitle": "Quick Actions", + "quickActionsProviderLocal": "Local", + "quickActionsProviderLDAP": "LDAP", + "quickActionsProviderOAuth": "{{provider}} OAuth" } From 93f882e460db7ac20dcf08f94b554ca3a1a5d6b1 Mon Sep 17 00:00:00 2001 From: Stavros Date: Wed, 24 Jun 2026 12:48:22 +0300 Subject: [PATCH 2/2] fix: don't allow the reserved provider names to be used in oauth --- internal/bootstrap/app_bootstrap.go | 5 +++++ internal/model/constants.go | 2 ++ 2 files changed, 7 insertions(+) diff --git a/internal/bootstrap/app_bootstrap.go b/internal/bootstrap/app_bootstrap.go index c24638f5..698c019e 100644 --- a/internal/bootstrap/app_bootstrap.go +++ b/internal/bootstrap/app_bootstrap.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "os/signal" + "slices" "sort" "strings" "syscall" @@ -131,6 +132,10 @@ func (app *BootstrapApp) Setup() error { app.runtime.OAuthProviders = app.config.OAuth.Providers for id, provider := range app.runtime.OAuthProviders { + if slices.Contains(model.ReservedProviderNames, id) { + return fmt.Errorf("provider id %s is reserved and cannot be used", id) + } + providerWhitelist, err := utils.GetStringList(provider.Whitelist, provider.WhitelistFile) if err != nil { return fmt.Errorf("failed to load oauth whitelist for provider %s: %w", id, err) diff --git a/internal/model/constants.go b/internal/model/constants.go index d5885dcf..ff44a729 100644 --- a/internal/model/constants.go +++ b/internal/model/constants.go @@ -17,6 +17,8 @@ var OverrideProviders = map[string]string{ "github": "GitHub", } +var ReservedProviderNames = []string{"local", "ldap", "tailscale"} + const SessionCookieName = "tinyauth-session" const CSRFCookieName = "tinyauth-csrf" const RedirectCookieName = "tinyauth-redirect"