mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-11-03 15:45:51 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v3.4.0-bet
			...
			v3.4.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					f2c81b6a5c | ||
| 
						 | 
					34c8d16c7d | 
@@ -1,7 +1,8 @@
 | 
			
		||||
import { useAppContext } from "@/context/app-context";
 | 
			
		||||
import { LanguageSelector } from "../language/language";
 | 
			
		||||
import { Outlet } from "react-router";
 | 
			
		||||
 | 
			
		||||
export const Layout = ({ children }: { children: React.ReactNode }) => {
 | 
			
		||||
export const Layout = () => {
 | 
			
		||||
  const { backgroundImage } = useAppContext();
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@@ -14,7 +15,7 @@ export const Layout = ({ children }: { children: React.ReactNode }) => {
 | 
			
		||||
      }}
 | 
			
		||||
    >
 | 
			
		||||
      <LanguageSelector />
 | 
			
		||||
      {children}
 | 
			
		||||
      <Outlet />
 | 
			
		||||
    </div>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -136,7 +136,7 @@ h4 {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
p {
 | 
			
		||||
  @apply leading-7 [&:not(:first-child)]:mt-6;
 | 
			
		||||
  @apply leading-6 [&:not(:first-child)]:mt-6;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
blockquote {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import { StrictMode } from "react";
 | 
			
		||||
import { createRoot } from "react-dom/client";
 | 
			
		||||
import "./index.css";
 | 
			
		||||
import { Layout } from "./components/layout/layout.tsx";
 | 
			
		||||
import { createBrowserRouter, RouterProvider } from "react-router";
 | 
			
		||||
import { BrowserRouter, Route, Routes } from "react-router";
 | 
			
		||||
import { LoginPage } from "./pages/login-page.tsx";
 | 
			
		||||
import { App } from "./App.tsx";
 | 
			
		||||
import { ErrorPage } from "./pages/error-page.tsx";
 | 
			
		||||
@@ -17,54 +17,6 @@ import { AppContextProvider } from "./context/app-context.tsx";
 | 
			
		||||
import { UserContextProvider } from "./context/user-context.tsx";
 | 
			
		||||
import { Toaster } from "@/components/ui/sonner";
 | 
			
		||||
 | 
			
		||||
const router = createBrowserRouter([
 | 
			
		||||
  {
 | 
			
		||||
    path: "/",
 | 
			
		||||
    element: <App />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/login",
 | 
			
		||||
    element: <LoginPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/logout",
 | 
			
		||||
    element: <LogoutPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/continue",
 | 
			
		||||
    element: <ContinuePage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/totp",
 | 
			
		||||
    element: <TotpPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/forgot-password",
 | 
			
		||||
    element: <ForgotPasswordPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/unauthorized",
 | 
			
		||||
    element: <UnauthorizedPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "/error",
 | 
			
		||||
    element: <ErrorPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    path: "*",
 | 
			
		||||
    element: <NotFoundPage />,
 | 
			
		||||
    errorElement: <ErrorPage />,
 | 
			
		||||
  },
 | 
			
		||||
]);
 | 
			
		||||
 | 
			
		||||
const queryClient = new QueryClient();
 | 
			
		||||
 | 
			
		||||
createRoot(document.getElementById("root")!).render(
 | 
			
		||||
@@ -72,10 +24,25 @@ createRoot(document.getElementById("root")!).render(
 | 
			
		||||
    <QueryClientProvider client={queryClient}>
 | 
			
		||||
      <AppContextProvider>
 | 
			
		||||
        <UserContextProvider>
 | 
			
		||||
          <Layout>
 | 
			
		||||
            <RouterProvider router={router} />
 | 
			
		||||
          <BrowserRouter>
 | 
			
		||||
            <Routes>
 | 
			
		||||
              <Route element={<Layout />} errorElement={<ErrorPage />}>
 | 
			
		||||
                <Route path="/" element={<App />} />
 | 
			
		||||
                <Route path="/login" element={<LoginPage />} />
 | 
			
		||||
                <Route path="/logout" element={<LogoutPage />} />
 | 
			
		||||
                <Route path="/continue" element={<ContinuePage />} />
 | 
			
		||||
                <Route path="/totp" element={<TotpPage />} />
 | 
			
		||||
                <Route
 | 
			
		||||
                  path="/forgot-password"
 | 
			
		||||
                  element={<ForgotPasswordPage />}
 | 
			
		||||
                />
 | 
			
		||||
                <Route path="/unauthorized" element={<UnauthorizedPage />} />
 | 
			
		||||
                <Route path="/error" element={<ErrorPage />} />
 | 
			
		||||
                <Route path="*" element={<NotFoundPage />} />
 | 
			
		||||
              </Route>
 | 
			
		||||
            </Routes>
 | 
			
		||||
          </BrowserRouter>
 | 
			
		||||
          <Toaster />
 | 
			
		||||
          </Layout>
 | 
			
		||||
        </UserContextProvider>
 | 
			
		||||
      </AppContextProvider>
 | 
			
		||||
    </QueryClientProvider>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import { isValidUrl } from "@/lib/utils";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Navigate, useLocation, useNavigate } from "react-router";
 | 
			
		||||
import DOMPurify from "dompurify";
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
 | 
			
		||||
export const ContinuePage = () => {
 | 
			
		||||
  const { isLoggedIn } = useUserContext();
 | 
			
		||||
@@ -22,6 +23,7 @@ export const ContinuePage = () => {
 | 
			
		||||
 | 
			
		||||
  const { domain, disableContinue } = useAppContext();
 | 
			
		||||
  const { search } = useLocation();
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const searchParams = new URLSearchParams(search);
 | 
			
		||||
  const redirectURI = searchParams.get("redirect_uri");
 | 
			
		||||
@@ -34,10 +36,15 @@ export const ContinuePage = () => {
 | 
			
		||||
    return <Navigate to="/logout" />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (disableContinue) {
 | 
			
		||||
  const handleRedirect = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    window.location.href = DOMPurify.sanitize(redirectURI);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (disableContinue) {
 | 
			
		||||
    handleRedirect();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
 | 
			
		||||
@@ -63,14 +70,13 @@ export const ContinuePage = () => {
 | 
			
		||||
        </CardHeader>
 | 
			
		||||
        <CardFooter className="flex flex-col items-stretch gap-2">
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={() =>
 | 
			
		||||
              (window.location.href = DOMPurify.sanitize(redirectURI))
 | 
			
		||||
            }
 | 
			
		||||
            onClick={handleRedirect}
 | 
			
		||||
            loading={loading}
 | 
			
		||||
            variant="destructive"
 | 
			
		||||
          >
 | 
			
		||||
            {t("continueTitle")}
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button onClick={() => navigate("/logout")} variant="outline">
 | 
			
		||||
          <Button onClick={() => navigate("/logout")} variant="outline" disabled={loading}>
 | 
			
		||||
            {t("cancelTitle")}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </CardFooter>
 | 
			
		||||
@@ -97,14 +103,13 @@ export const ContinuePage = () => {
 | 
			
		||||
        </CardHeader>
 | 
			
		||||
        <CardFooter className="flex flex-col items-stretch gap-2">
 | 
			
		||||
          <Button
 | 
			
		||||
            onClick={() =>
 | 
			
		||||
              (window.location.href = DOMPurify.sanitize(redirectURI))
 | 
			
		||||
            }
 | 
			
		||||
            onClick={handleRedirect}
 | 
			
		||||
            loading={loading}
 | 
			
		||||
            variant="warning"
 | 
			
		||||
          >
 | 
			
		||||
            {t("continueTitle")}
 | 
			
		||||
          </Button>
 | 
			
		||||
          <Button onClick={() => navigate("/logout")} variant="outline">
 | 
			
		||||
          <Button onClick={() => navigate("/logout")} variant="outline" disabled={loading}>
 | 
			
		||||
            {t("cancelTitle")}
 | 
			
		||||
          </Button>
 | 
			
		||||
        </CardFooter>
 | 
			
		||||
@@ -120,9 +125,8 @@ export const ContinuePage = () => {
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardFooter className="flex flex-col items-stretch">
 | 
			
		||||
        <Button
 | 
			
		||||
          onClick={() =>
 | 
			
		||||
            (window.location.href = DOMPurify.sanitize(redirectURI))
 | 
			
		||||
          }
 | 
			
		||||
          onClick={handleRedirect}
 | 
			
		||||
          loading={loading}
 | 
			
		||||
        >
 | 
			
		||||
          {t("continueTitle")}
 | 
			
		||||
        </Button>
 | 
			
		||||
 
 | 
			
		||||
@@ -126,6 +126,8 @@ export const LoginPage = () => {
 | 
			
		||||
                icon={<GoogleIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
                onClick={() => oauthMutation.mutate("google")}
 | 
			
		||||
                loading={oauthMutation.isPending && oauthMutation.variables === "google"}
 | 
			
		||||
                disabled={oauthMutation.isPending || loginMutation.isPending}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {configuredProviders.includes("github") && (
 | 
			
		||||
@@ -134,6 +136,8 @@ export const LoginPage = () => {
 | 
			
		||||
                icon={<GithubIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
                onClick={() => oauthMutation.mutate("github")}
 | 
			
		||||
                loading={oauthMutation.isPending && oauthMutation.variables === "github"}
 | 
			
		||||
                disabled={oauthMutation.isPending || loginMutation.isPending}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
            {configuredProviders.includes("generic") && (
 | 
			
		||||
@@ -142,6 +146,8 @@ export const LoginPage = () => {
 | 
			
		||||
                icon={<GenericIcon />}
 | 
			
		||||
                className="w-full"
 | 
			
		||||
                onClick={() => oauthMutation.mutate("generic")}
 | 
			
		||||
                loading={oauthMutation.isPending && oauthMutation.variables === "generic"}
 | 
			
		||||
                disabled={oauthMutation.isPending || loginMutation.isPending}
 | 
			
		||||
              />
 | 
			
		||||
            )}
 | 
			
		||||
          </div>
 | 
			
		||||
@@ -152,7 +158,7 @@ export const LoginPage = () => {
 | 
			
		||||
        {userAuthConfigured && (
 | 
			
		||||
          <LoginForm
 | 
			
		||||
            onSubmit={(values) => loginMutation.mutate(values)}
 | 
			
		||||
            loading={loginMutation.isPending}
 | 
			
		||||
            loading={loginMutation.isPending || oauthMutation.isPending}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
        {configuredProviders.length == 0 && (
 | 
			
		||||
 
 | 
			
		||||
@@ -6,12 +6,19 @@ import {
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { useNavigate } from "react-router";
 | 
			
		||||
 | 
			
		||||
export const NotFoundPage = () => {
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleRedirect = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    navigate("/");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Card className="min-w-xs sm:min-w-sm">
 | 
			
		||||
@@ -20,7 +27,7 @@ export const NotFoundPage = () => {
 | 
			
		||||
        <CardDescription>{t("notFoundSubtitle")}</CardDescription>
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardFooter className="flex flex-col items-stretch">
 | 
			
		||||
        <Button onClick={() => navigate("/")}>{t("notFoundButton")}</Button>
 | 
			
		||||
        <Button onClick={handleRedirect} loading={loading}>{t("notFoundButton")}</Button>
 | 
			
		||||
      </CardFooter>
 | 
			
		||||
    </Card>
 | 
			
		||||
  );
 | 
			
		||||
 
 | 
			
		||||
@@ -8,18 +8,24 @@ import {
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { useUserContext } from "@/context/user-context";
 | 
			
		||||
import { TotpSchema } from "@/schemas/totp-schema";
 | 
			
		||||
import { useMutation } from "@tanstack/react-query";
 | 
			
		||||
import axios from "axios";
 | 
			
		||||
import { useId } from "react";
 | 
			
		||||
import { useTranslation } from "react-i18next";
 | 
			
		||||
import { useLocation, useNavigate } from "react-router";
 | 
			
		||||
import { Navigate, useLocation } from "react-router";
 | 
			
		||||
import { toast } from "sonner";
 | 
			
		||||
 | 
			
		||||
export const TotpPage = () => {
 | 
			
		||||
  const { totpPending } = useUserContext();
 | 
			
		||||
 | 
			
		||||
  if (!totpPending) {
 | 
			
		||||
    return <Navigate to="/" />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const { search } = useLocation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const formId = useId();
 | 
			
		||||
 | 
			
		||||
  const searchParams = new URLSearchParams(search);
 | 
			
		||||
@@ -34,7 +40,7 @@ export const TotpPage = () => {
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      setTimeout(() => {
 | 
			
		||||
        navigate(
 | 
			
		||||
        window.location.replace(
 | 
			
		||||
          `/continue?redirect_uri=${encodeURIComponent(redirectUri ?? "")}`,
 | 
			
		||||
        );
 | 
			
		||||
      }, 500);
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ import {
 | 
			
		||||
  CardHeader,
 | 
			
		||||
  CardTitle,
 | 
			
		||||
} from "@/components/ui/card";
 | 
			
		||||
import { useState } from "react";
 | 
			
		||||
import { Trans, useTranslation } from "react-i18next";
 | 
			
		||||
import { Navigate, useLocation, useNavigate } from "react-router";
 | 
			
		||||
 | 
			
		||||
@@ -23,6 +24,12 @@ export const UnauthorizedPage = () => {
 | 
			
		||||
 | 
			
		||||
  const { t } = useTranslation();
 | 
			
		||||
  const navigate = useNavigate();
 | 
			
		||||
  const [loading, setLoading] = useState(false);
 | 
			
		||||
 | 
			
		||||
  const handleRedirect = () => {
 | 
			
		||||
    setLoading(true);
 | 
			
		||||
    navigate("/login");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  let i18nKey = "unauthorizedLoginSubtitle";
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +60,7 @@ export const UnauthorizedPage = () => {
 | 
			
		||||
        </CardDescription>
 | 
			
		||||
      </CardHeader>
 | 
			
		||||
      <CardFooter className="flex flex-col items-stretch">
 | 
			
		||||
        <Button onClick={() => navigate("/login")}>
 | 
			
		||||
        <Button onClick={handleRedirect} loading={loading}>
 | 
			
		||||
          {t("unauthorizedButton")}
 | 
			
		||||
        </Button>
 | 
			
		||||
      </CardFooter>
 | 
			
		||||
 
 | 
			
		||||
@@ -255,16 +255,16 @@ func ParseUser(user string) (types.User, error) {
 | 
			
		||||
	// Check if the user has a totp secret
 | 
			
		||||
	if len(userSplit) == 2 {
 | 
			
		||||
		return types.User{
 | 
			
		||||
			Username: userSplit[0],
 | 
			
		||||
			Password: userSplit[1],
 | 
			
		||||
			Username: strings.TrimSpace(userSplit[0]),
 | 
			
		||||
			Password: strings.TrimSpace(userSplit[1]),
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return the user struct
 | 
			
		||||
	return types.User{
 | 
			
		||||
		Username:   userSplit[0],
 | 
			
		||||
		Password:   userSplit[1],
 | 
			
		||||
		TotpSecret: userSplit[2],
 | 
			
		||||
		Username:   strings.TrimSpace(userSplit[0]),
 | 
			
		||||
		Password:   strings.TrimSpace(userSplit[1]),
 | 
			
		||||
		TotpSecret: strings.TrimSpace(userSplit[2]),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user