mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 06:05:43 +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