mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-06-24 20:30:16 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 93f882e460 | |||
| aaa5f4cb2f |
@@ -0,0 +1,22 @@
|
|||||||
|
import type { SVGProps } from "react";
|
||||||
|
|
||||||
|
export function LocalAuthIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth={2}
|
||||||
|
d="M8 7a4 4 0 1 0 8 0a4 4 0 0 0-8 0M6 21v-2a4 4 0 0 1 4-4h5m3.5 3.5L15 22l-1.5-1.5m5.054-2.086a2 2 0 1 1 2.828-2.828a2 2 0 0 1-2.828 2.828M16 19l1 1"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -37,6 +37,13 @@ import { useMutation } from "@tanstack/react-query";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useEffect } from "react";
|
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 }) {
|
function Avatar({ initial }: { initial: string }) {
|
||||||
return (
|
return (
|
||||||
@@ -49,8 +56,18 @@ function Avatar({ initial }: { initial: string }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const iconStyles = "size-4";
|
||||||
|
|
||||||
|
const iconMap: Record<string, React.ReactNode> = {
|
||||||
|
google: <GoogleIcon className={iconStyles} />,
|
||||||
|
github: <GithubIcon className={iconStyles} />,
|
||||||
|
tailscale: <TailscaleIcon className={iconStyles} />,
|
||||||
|
microsoft: <MicrosoftIcon className={iconStyles} />,
|
||||||
|
pocketid: <PocketIDIcon className={iconStyles} />,
|
||||||
|
};
|
||||||
|
|
||||||
export const QuickActions = () => {
|
export const QuickActions = () => {
|
||||||
const { auth } = useUserContext();
|
const { auth, oauth, tailscale } = useUserContext();
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { search } = useLocation();
|
const { search } = useLocation();
|
||||||
@@ -64,6 +81,41 @@ export const QuickActions = () => {
|
|||||||
const screenParams = useScreenParams(searchParams);
|
const screenParams = useScreenParams(searchParams);
|
||||||
const compiledParams = recompileScreenParams(screenParams);
|
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: <LocalAuthIcon className={iconStyles} />,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oauth.active) {
|
||||||
|
return {
|
||||||
|
name: t("quickActionsProviderOAuth", { provider: oauth.displayName }),
|
||||||
|
icon: iconMap[auth.providerId] || <OAuthIcon className={iconStyles} />,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.providerId === "tailscale") {
|
||||||
|
return {
|
||||||
|
name: `Tailscale (${tailscale.nodeName})`,
|
||||||
|
icon: <TailscaleIcon className={iconStyles} />,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
})();
|
||||||
|
|
||||||
const logoutMutation = useMutation({
|
const logoutMutation = useMutation({
|
||||||
mutationFn: () => axios.post("/api/user/logout"),
|
mutationFn: () => axios.post("/api/user/logout"),
|
||||||
mutationKey: ["logout"],
|
mutationKey: ["logout"],
|
||||||
@@ -134,11 +186,19 @@ export const QuickActions = () => {
|
|||||||
<div className="bg-foreground text-background flex size-9 shrink-0 items-center justify-center rounded-full text-sm font-medium">
|
<div className="bg-foreground text-background flex size-9 shrink-0 items-center justify-center rounded-full text-sm font-medium">
|
||||||
{initial}
|
{initial}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex min-w-0 flex-col">
|
<div className="flex min-w-0 flex-col gap-1.5">
|
||||||
<span className="truncate text-sm font-medium">
|
<div className="flex items-center gap-2">
|
||||||
{auth.name}
|
<span className="truncate text-sm font-medium leading-none">
|
||||||
</span>
|
{auth.name}
|
||||||
<span className="text-muted-foreground truncate text-xs font-normal">
|
</span>
|
||||||
|
{providerDetails && (
|
||||||
|
<span className="text-muted-foreground inline-flex shrink-0 items-center gap-1 rounded-lg bg-secondary px-2 py-1.25 text-[10px] font-medium leading-none [&_svg]:size-3">
|
||||||
|
{providerDetails.icon}
|
||||||
|
{providerDetails.name}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<span className="text-muted-foreground truncate text-xs leading-none">
|
||||||
{auth.email}
|
{auth.email}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -99,5 +99,8 @@
|
|||||||
"quickActionsThemeDark": "Dark",
|
"quickActionsThemeDark": "Dark",
|
||||||
"quickActionsThemeSystem": "System",
|
"quickActionsThemeSystem": "System",
|
||||||
"quickActionsLogout": "Logout",
|
"quickActionsLogout": "Logout",
|
||||||
"quickActionsTitle": "Quick Actions"
|
"quickActionsTitle": "Quick Actions",
|
||||||
|
"quickActionsProviderLocal": "Local",
|
||||||
|
"quickActionsProviderLDAP": "LDAP",
|
||||||
|
"quickActionsProviderOAuth": "{{provider}} OAuth"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"slices"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -131,6 +132,10 @@ func (app *BootstrapApp) Setup() error {
|
|||||||
app.runtime.OAuthProviders = app.config.OAuth.Providers
|
app.runtime.OAuthProviders = app.config.OAuth.Providers
|
||||||
|
|
||||||
for id, provider := range app.runtime.OAuthProviders {
|
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)
|
providerWhitelist, err := utils.GetStringList(provider.Whitelist, provider.WhitelistFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to load oauth whitelist for provider %s: %w", id, err)
|
return fmt.Errorf("failed to load oauth whitelist for provider %s: %w", id, err)
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ var OverrideProviders = map[string]string{
|
|||||||
"github": "GitHub",
|
"github": "GitHub",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ReservedProviderNames = []string{"local", "ldap", "tailscale"}
|
||||||
|
|
||||||
const SessionCookieName = "tinyauth-session"
|
const SessionCookieName = "tinyauth-session"
|
||||||
const CSRFCookieName = "tinyauth-csrf"
|
const CSRFCookieName = "tinyauth-csrf"
|
||||||
const RedirectCookieName = "tinyauth-redirect"
|
const RedirectCookieName = "tinyauth-redirect"
|
||||||
|
|||||||
Reference in New Issue
Block a user