diff --git a/frontend/src/components/language/language.tsx b/frontend/src/components/language/language.tsx deleted file mode 100644 index 3f0bf57a..00000000 --- a/frontend/src/components/language/language.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { languages, SupportedLanguage } from "@/lib/i18n/locales"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "../ui/select"; -import { useState } from "react"; -import i18n from "@/lib/i18n/i18n"; - -export const LanguageSelector = () => { - const [language, setLanguage] = useState( - i18n.language as SupportedLanguage, - ); - - const handleSelect = (option: string) => { - setLanguage(option as SupportedLanguage); - i18n.changeLanguage(option as SupportedLanguage); - }; - - return ( - - ); -}; diff --git a/frontend/src/components/layout/layout.tsx b/frontend/src/components/layout/layout.tsx index d59aadf3..e129092e 100644 --- a/frontend/src/components/layout/layout.tsx +++ b/frontend/src/components/layout/layout.tsx @@ -1,9 +1,8 @@ import { useAppContext } from "@/context/app-context"; -import { LanguageSelector } from "../language/language"; import { Outlet } from "react-router"; import { useCallback, useEffect, useState } from "react"; import { DomainWarning } from "../domain-warning/domain-warning"; -import { ThemeToggle } from "../theme-toggle/theme-toggle"; +import { QuickActions } from "../quick-actions/quick-actions"; const BaseLayout = ({ children }: { children: React.ReactNode }) => { const { ui } = useAppContext(); @@ -21,9 +20,8 @@ const BaseLayout = ({ children }: { children: React.ReactNode }) => { backgroundPosition: "center", }} > -
- - +
+
{children}
diff --git a/frontend/src/components/quick-actions/quick-actions.tsx b/frontend/src/components/quick-actions/quick-actions.tsx new file mode 100644 index 00000000..6c44b75f --- /dev/null +++ b/frontend/src/components/quick-actions/quick-actions.tsx @@ -0,0 +1,205 @@ +import { languages, SupportedLanguage } from "@/lib/i18n/locales"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuPortal, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; +import { useState } from "react"; +import i18n from "@/lib/i18n/i18n"; +import { useUserContext } from "@/context/user-context"; +import { ScrollArea } from "../ui/scroll-area"; +import { useTheme } from "../providers/theme-provider"; +import { + Check, + DoorOpenIcon, + Languages, + Monitor, + Moon, + Palette, + Settings, + Sun, +} from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { useLocation } from "react-router"; +import { useRef } from "react"; +import { + useScreenParams, + recompileScreenParams, +} from "@/lib/hooks/screen-params"; +import { useMutation } from "@tanstack/react-query"; +import axios from "axios"; +import { toast } from "sonner"; +import { useEffect } from "react"; + +function Avatar({ initial }: { initial: string }) { + return ( + + + + {initial} + + + ); +} + +export const QuickActions = () => { + const { auth } = useUserContext(); + const { theme, setTheme } = useTheme(); + const { t } = useTranslation(); + const { search } = useLocation(); + + const [language, setLanguage] = useState( + i18n.language as SupportedLanguage, + ); + + const redirectTimer = useRef(null); + const searchParams = new URLSearchParams(search); + const screenParams = useScreenParams(searchParams); + const compiledParams = recompileScreenParams(screenParams); + + const logoutMutation = useMutation({ + mutationFn: () => axios.post("/api/user/logout"), + mutationKey: ["logout"], + onSuccess: () => { + toast.success(t("logoutSuccessTitle"), { + description: t("logoutSuccessSubtitle"), + }); + + redirectTimer.current = window.setTimeout(() => { + window.location.replace(`/login${compiledParams}`); + }, 500); + }, + onError: () => { + toast.error(t("logoutFailTitle"), { + description: t("logoutFailSubtitle"), + }); + }, + }); + + useEffect(() => { + return () => { + if (redirectTimer.current) { + clearTimeout(redirectTimer.current); + } + }; + }, [redirectTimer]); + + const initial = auth.authenticated + ? (auth.name[0] || "U").toUpperCase() + : null; + + const handleSelect = (option: string) => { + setLanguage(option as SupportedLanguage); + i18n.changeLanguage(option as SupportedLanguage); + }; + + const themes = [ + { key: "light", label: t("quickActionsThemeLight"), icon: Sun }, + { key: "dark", label: t("quickActionsThemeDark"), icon: Moon }, + { key: "system", label: t("quickActionsThemeSystem"), icon: Monitor }, + ] as const; + + return ( + + + + + + + {auth.authenticated && ( + <> + +
+ {initial} +
+
+ + {auth.name} + + + {auth.email} + +
+
+ + + + )} + + + + + {t("quickActionsLanguage")} + + + + + {Object.entries(languages).map(([key, value]) => ( + handleSelect(key)} + > + {value} + {language === key && } + + ))} + + + + + + + + + {t("quickActionsTheme")} + + + + {themes.map(({ key, label, icon: Icon }) => ( + setTheme(key)}> + + + {label} + + {theme === key && } + + ))} + + + + + {auth.authenticated && ( + <> + + logoutMutation.mutate()} + className="text-destructive" + > + + {t("quickActionsLogout")} + + + )} +
+
+ ); +}; diff --git a/frontend/src/components/theme-toggle/theme-toggle.tsx b/frontend/src/components/theme-toggle/theme-toggle.tsx deleted file mode 100644 index c0791cfb..00000000 --- a/frontend/src/components/theme-toggle/theme-toggle.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { Moon, Sun } from "lucide-react"; - -import { Button } from "@/components/ui/button"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { useTheme } from "@/components/providers/theme-provider"; - -export function ThemeToggle() { - const { setTheme } = useTheme(); - - return ( - - - - - - setTheme("light")}> - Light - - setTheme("dark")}> - Dark - - setTheme("system")}> - System - - - - ); -} diff --git a/frontend/src/components/ui/scroll-area.tsx b/frontend/src/components/ui/scroll-area.tsx new file mode 100644 index 00000000..e38a492f --- /dev/null +++ b/frontend/src/components/ui/scroll-area.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { ScrollArea as ScrollAreaPrimitive } from "radix-ui" + +import { cn } from "@/lib/utils" + +function ScrollArea({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + {children} + + + + + ) +} + +function ScrollBar({ + className, + orientation = "vertical", + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { ScrollArea, ScrollBar } diff --git a/frontend/src/lib/hooks/login-for.ts b/frontend/src/lib/hooks/login-for.ts new file mode 100644 index 00000000..8cf11579 --- /dev/null +++ b/frontend/src/lib/hooks/login-for.ts @@ -0,0 +1,17 @@ +type UseLoginForProps = { + login_for?: "oidc" | "app"; + compiledParams: string; +}; + +export const useLoginFor = (props: UseLoginForProps): string => { + const { login_for, compiledParams } = props; + + switch (login_for) { + case "oidc": + return "/oidc/authorize" + compiledParams; + case "app": + return "/continue" + compiledParams; + default: + return "/logout"; + } +}; diff --git a/frontend/src/lib/hooks/redirect-uri.ts b/frontend/src/lib/hooks/redirect-uri.ts index 5211178a..aeeae0c5 100644 --- a/frontend/src/lib/hooks/redirect-uri.ts +++ b/frontend/src/lib/hooks/redirect-uri.ts @@ -7,7 +7,7 @@ type IuseRedirectUri = { }; export const useRedirectUri = ( - redirect_uri: string | null, + redirect_uri: string | undefined, cookieDomain: string, ): IuseRedirectUri => { let isValid = false; @@ -15,7 +15,7 @@ export const useRedirectUri = ( let isAllowedProto = false; let isHttpsDowngrade = false; - if (!redirect_uri) { + if (redirect_uri === undefined) { return { valid: isValid, trusted: isTrusted, diff --git a/frontend/src/lib/hooks/screen-params.ts b/frontend/src/lib/hooks/screen-params.ts index bde309c7..9a22d75f 100644 --- a/frontend/src/lib/hooks/screen-params.ts +++ b/frontend/src/lib/hooks/screen-params.ts @@ -2,7 +2,7 @@ import { z } from "zod"; type ScreenParams = { login_for?: "oidc" | "app"; - redirect_url?: string; + redirect_uri?: string; oidc_ticket?: string; oidc_scope?: string; oidc_name?: string; @@ -10,7 +10,7 @@ type ScreenParams = { const zodScreenParams = z.object({ login_for: z.enum(["oidc", "app"]).optional(), - redirect_url: z.string().optional(), + redirect_uri: z.string().optional(), oidc_ticket: z.string().optional(), oidc_scope: z.string().optional(), oidc_name: z.string().optional(), diff --git a/frontend/src/lib/i18n/locales/en-US.json b/frontend/src/lib/i18n/locales/en-US.json index a71696e2..7b0d63af 100644 --- a/frontend/src/lib/i18n/locales/en-US.json +++ b/frontend/src/lib/i18n/locales/en-US.json @@ -1,96 +1,102 @@ { - "loginTitle": "Welcome back, login with", - "loginTitleSimple": "Welcome back, please login", - "loginDivider": "Or", - "loginUsername": "Username", - "loginPassword": "Password", - "loginSubmit": "Login", - "loginFailTitle": "Failed to log in", - "loginFailSubtitle": "Please check your username and password", - "loginFailRateLimit": "You failed to login too many times. Please try again later", - "loginSuccessTitle": "Logged in", - "loginSuccessSubtitle": "Welcome back!", - "loginOauthFailTitle": "An error occurred", - "loginOauthFailSubtitle": "Failed to get OAuth URL", - "loginOauthSuccessTitle": "Redirecting", - "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", - "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", - "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", - "loginOauthAutoRedirectButton": "Redirect now", - "continueTitle": "Continue", - "continueRedirectingTitle": "Redirecting...", - "continueRedirectingSubtitle": "You should be redirected to the app soon", - "continueRedirectManually": "Redirect me manually", - "continueInsecureRedirectTitle": "Insecure redirect", - "continueInsecureRedirectSubtitle": "You are trying to redirect from https to http which is not secure. Are you sure you want to continue?", - "continueUntrustedRedirectTitle": "Untrusted redirect", - "continueUntrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain ({{cookieDomain}}). Are you sure you want to continue?", - "logoutFailTitle": "Failed to log out", - "logoutFailSubtitle": "Please try again", - "logoutSuccessTitle": "Logged out", - "logoutSuccessSubtitle": "You have been logged out", - "logoutTitle": "Logout", - "logoutUsernameSubtitle": "You are currently logged in as {{username}}. Click the button below to logout.", - "logoutOauthSubtitle": "You are currently logged in as {{username}} using the {{provider}} OAuth provider. Click the button below to logout.", - "notFoundTitle": "Page not found", - "notFoundSubtitle": "The page you are looking for does not exist.", - "notFoundButton": "Go home", - "totpFailTitle": "Failed to verify code", - "totpFailSubtitle": "Please check your code and try again", - "totpSuccessTitle": "Verified", - "totpSuccessSubtitle": "Redirecting to your app", - "totpTitle": "Enter your TOTP code", - "totpSubtitle": "Please enter the code from your authenticator app.", - "unauthorizedTitle": "Unauthorized", - "unauthorizedResourceSubtitle": "The user with username {{username}} is not authorized to access the resource {{resource}}.", - "unauthorizedLoginSubtitle": "The user with username {{username}} is not authorized to login.", - "unauthorizedGroupsSubtitle": "The user with username {{username}} is not in the groups required by the resource {{resource}}.", - "unauthorizedIpSubtitle": "Your IP address {{ip}} is not authorized to access the resource {{resource}}.", - "unauthorizedButton": "Try again", - "cancelTitle": "Cancel", - "forgotPasswordTitle": "Forgot your password?", - "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", - "errorTitle": "An error occurred", - "errorSubtitleInfo": "The following error occurred while processing your request:", - "errorSubtitle": "An error occurred while trying to perform this action. Please check your browser console or the app logs for more information.", - "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", - "fieldRequired": "This field is required", - "invalidInput": "Invalid input", - "domainWarningTitle": "Invalid Domain", - "domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.", - "domainWarningCurrent": "Current:", - "domainWarningExpected": "Expected:", - "ignoreTitle": "Ignore", - "goToCorrectDomainTitle": "Go to correct domain", - "authorizeTitle": "Authorize", - "authorizeCardTitle": "Continue to {{app}}?", - "authorizeSubtitle": "Would you like to continue to this app? Please carefully review the permissions requested by the app.", - "authorizeSubtitleOAuth": "Would you like to continue to this app?", - "authorizeLoadingTitle": "Loading...", - "authorizeLoadingSubtitle": "Please wait while we load the client information.", - "authorizeSuccessTitle": "Authorized", - "authorizeSuccessSubtitle": "You will be redirected to the app in a few seconds.", - "authorizeErrorClientInfo": "An error occurred while loading the client information. Please try again later.", - "authorizeErrorMissingParams": "The following parameters are missing: {{missingParams}}", - "openidScopeName": "OpenID Connect", - "openidScopeDescription": "Allows the app to access your OpenID Connect information.", - "emailScopeName": "Email", - "emailScopeDescription": "Allows the app to access your email address.", - "profileScopeName": "Profile", - "profileScopeDescription": "Allows the app to access your profile information.", - "groupsScopeName": "Groups", - "groupsScopeDescription": "Allows the app to access your group information.", - "backToLoginButton": "Back to login", - "phoneScopeName": "Phone", - "phoneScopeDescription": "Allows the app to access your phone number.", - "addressScopeName": "Address", - "addressScopeDescription": "Allows the app to access your address.", - "loginTailscaleTitle": "Continue with Tailscale", - "loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?", - "loginTailscaleDeviceName": "Device name:", - "loginTailscaleSubmit": "Continue with Tailscale", - "loginTailscaleOtherMethod": "Login with another method", - "loginTailscaleSuccess": "Successfully authenticated with Tailscale.", - "loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.", - "logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device {{deviceName}}. Click the button below to logout." + "loginTitle": "Welcome back, login with", + "loginTitleSimple": "Welcome back, please login", + "loginDivider": "Or", + "loginUsername": "Username", + "loginPassword": "Password", + "loginSubmit": "Login", + "loginFailTitle": "Failed to log in", + "loginFailSubtitle": "Please check your username and password", + "loginFailRateLimit": "You failed to login too many times. Please try again later", + "loginSuccessTitle": "Logged in", + "loginSuccessSubtitle": "Welcome back!", + "loginOauthFailTitle": "An error occurred", + "loginOauthFailSubtitle": "Failed to get OAuth URL", + "loginOauthSuccessTitle": "Redirecting", + "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", + "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", + "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", + "loginOauthAutoRedirectButton": "Redirect now", + "continueTitle": "Continue", + "continueRedirectingTitle": "Redirecting...", + "continueRedirectingSubtitle": "You should be redirected to the app soon", + "continueRedirectManually": "Redirect me manually", + "continueInsecureRedirectTitle": "Insecure redirect", + "continueInsecureRedirectSubtitle": "You are trying to redirect from https to http which is not secure. Are you sure you want to continue?", + "continueUntrustedRedirectTitle": "Untrusted redirect", + "continueUntrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain ({{cookieDomain}}). Are you sure you want to continue?", + "logoutFailTitle": "Failed to log out", + "logoutFailSubtitle": "Please try again", + "logoutSuccessTitle": "Logged out", + "logoutSuccessSubtitle": "You have been logged out", + "logoutTitle": "Logout", + "logoutUsernameSubtitle": "You are currently logged in as {{username}}. Click the button below to logout.", + "logoutOauthSubtitle": "You are currently logged in as {{username}} using the {{provider}} OAuth provider. Click the button below to logout.", + "notFoundTitle": "Page not found", + "notFoundSubtitle": "The page you are looking for does not exist.", + "notFoundButton": "Go home", + "totpFailTitle": "Failed to verify code", + "totpFailSubtitle": "Please check your code and try again", + "totpSuccessTitle": "Verified", + "totpSuccessSubtitle": "Redirecting to your app", + "totpTitle": "Enter your TOTP code", + "totpSubtitle": "Please enter the code from your authenticator app.", + "unauthorizedTitle": "Unauthorized", + "unauthorizedResourceSubtitle": "The user with username {{username}} is not authorized to access the resource {{resource}}.", + "unauthorizedLoginSubtitle": "The user with username {{username}} is not authorized to login.", + "unauthorizedGroupsSubtitle": "The user with username {{username}} is not in the groups required by the resource {{resource}}.", + "unauthorizedIpSubtitle": "Your IP address {{ip}} is not authorized to access the resource {{resource}}.", + "unauthorizedButton": "Try again", + "cancelTitle": "Cancel", + "forgotPasswordTitle": "Forgot your password?", + "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", + "errorTitle": "An error occurred", + "errorSubtitleInfo": "The following error occurred while processing your request:", + "errorSubtitle": "An error occurred while trying to perform this action. Please check your browser console or the app logs for more information.", + "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", + "fieldRequired": "This field is required", + "invalidInput": "Invalid input", + "domainWarningTitle": "Invalid Domain", + "domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.", + "domainWarningCurrent": "Current:", + "domainWarningExpected": "Expected:", + "ignoreTitle": "Ignore", + "goToCorrectDomainTitle": "Go to correct domain", + "authorizeTitle": "Authorize", + "authorizeCardTitle": "Continue to {{app}}?", + "authorizeSubtitle": "Would you like to continue to this app? Please carefully review the permissions requested by the app.", + "authorizeSubtitleOAuth": "Would you like to continue to this app?", + "authorizeLoadingTitle": "Loading...", + "authorizeLoadingSubtitle": "Please wait while we load the client information.", + "authorizeSuccessTitle": "Authorized", + "authorizeSuccessSubtitle": "You will be redirected to the app in a few seconds.", + "authorizeErrorClientInfo": "An error occurred while loading the client information. Please try again later.", + "authorizeErrorMissingParams": "The following parameters are missing: {{missingParams}}", + "openidScopeName": "OpenID Connect", + "openidScopeDescription": "Allows the app to access your OpenID Connect information.", + "emailScopeName": "Email", + "emailScopeDescription": "Allows the app to access your email address.", + "profileScopeName": "Profile", + "profileScopeDescription": "Allows the app to access your profile information.", + "groupsScopeName": "Groups", + "groupsScopeDescription": "Allows the app to access your group information.", + "backToLoginButton": "Back to login", + "phoneScopeName": "Phone", + "phoneScopeDescription": "Allows the app to access your phone number.", + "addressScopeName": "Address", + "addressScopeDescription": "Allows the app to access your address.", + "loginTailscaleTitle": "Continue with Tailscale", + "loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?", + "loginTailscaleDeviceName": "Device name:", + "loginTailscaleSubmit": "Continue with Tailscale", + "loginTailscaleOtherMethod": "Login with another method", + "loginTailscaleSuccess": "Successfully authenticated with Tailscale.", + "loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.", + "logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device {{deviceName}}. Click the button below to logout.", + "quickActionsLanguage": "Language", + "quickActionsTheme": "Theme", + "quickActionsThemeLight": "Light", + "quickActionsThemeDark": "Dark", + "quickActionsThemeSystem": "System", + "quickActionsLogout": "Logout" } diff --git a/frontend/src/lib/i18n/locales/en.json b/frontend/src/lib/i18n/locales/en.json index a71696e2..7b0d63af 100644 --- a/frontend/src/lib/i18n/locales/en.json +++ b/frontend/src/lib/i18n/locales/en.json @@ -1,96 +1,102 @@ { - "loginTitle": "Welcome back, login with", - "loginTitleSimple": "Welcome back, please login", - "loginDivider": "Or", - "loginUsername": "Username", - "loginPassword": "Password", - "loginSubmit": "Login", - "loginFailTitle": "Failed to log in", - "loginFailSubtitle": "Please check your username and password", - "loginFailRateLimit": "You failed to login too many times. Please try again later", - "loginSuccessTitle": "Logged in", - "loginSuccessSubtitle": "Welcome back!", - "loginOauthFailTitle": "An error occurred", - "loginOauthFailSubtitle": "Failed to get OAuth URL", - "loginOauthSuccessTitle": "Redirecting", - "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", - "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", - "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", - "loginOauthAutoRedirectButton": "Redirect now", - "continueTitle": "Continue", - "continueRedirectingTitle": "Redirecting...", - "continueRedirectingSubtitle": "You should be redirected to the app soon", - "continueRedirectManually": "Redirect me manually", - "continueInsecureRedirectTitle": "Insecure redirect", - "continueInsecureRedirectSubtitle": "You are trying to redirect from https to http which is not secure. Are you sure you want to continue?", - "continueUntrustedRedirectTitle": "Untrusted redirect", - "continueUntrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain ({{cookieDomain}}). Are you sure you want to continue?", - "logoutFailTitle": "Failed to log out", - "logoutFailSubtitle": "Please try again", - "logoutSuccessTitle": "Logged out", - "logoutSuccessSubtitle": "You have been logged out", - "logoutTitle": "Logout", - "logoutUsernameSubtitle": "You are currently logged in as {{username}}. Click the button below to logout.", - "logoutOauthSubtitle": "You are currently logged in as {{username}} using the {{provider}} OAuth provider. Click the button below to logout.", - "notFoundTitle": "Page not found", - "notFoundSubtitle": "The page you are looking for does not exist.", - "notFoundButton": "Go home", - "totpFailTitle": "Failed to verify code", - "totpFailSubtitle": "Please check your code and try again", - "totpSuccessTitle": "Verified", - "totpSuccessSubtitle": "Redirecting to your app", - "totpTitle": "Enter your TOTP code", - "totpSubtitle": "Please enter the code from your authenticator app.", - "unauthorizedTitle": "Unauthorized", - "unauthorizedResourceSubtitle": "The user with username {{username}} is not authorized to access the resource {{resource}}.", - "unauthorizedLoginSubtitle": "The user with username {{username}} is not authorized to login.", - "unauthorizedGroupsSubtitle": "The user with username {{username}} is not in the groups required by the resource {{resource}}.", - "unauthorizedIpSubtitle": "Your IP address {{ip}} is not authorized to access the resource {{resource}}.", - "unauthorizedButton": "Try again", - "cancelTitle": "Cancel", - "forgotPasswordTitle": "Forgot your password?", - "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", - "errorTitle": "An error occurred", - "errorSubtitleInfo": "The following error occurred while processing your request:", - "errorSubtitle": "An error occurred while trying to perform this action. Please check your browser console or the app logs for more information.", - "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", - "fieldRequired": "This field is required", - "invalidInput": "Invalid input", - "domainWarningTitle": "Invalid Domain", - "domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.", - "domainWarningCurrent": "Current:", - "domainWarningExpected": "Expected:", - "ignoreTitle": "Ignore", - "goToCorrectDomainTitle": "Go to correct domain", - "authorizeTitle": "Authorize", - "authorizeCardTitle": "Continue to {{app}}?", - "authorizeSubtitle": "Would you like to continue to this app? Please carefully review the permissions requested by the app.", - "authorizeSubtitleOAuth": "Would you like to continue to this app?", - "authorizeLoadingTitle": "Loading...", - "authorizeLoadingSubtitle": "Please wait while we load the client information.", - "authorizeSuccessTitle": "Authorized", - "authorizeSuccessSubtitle": "You will be redirected to the app in a few seconds.", - "authorizeErrorClientInfo": "An error occurred while loading the client information. Please try again later.", - "authorizeErrorMissingParams": "The following parameters are missing: {{missingParams}}", - "openidScopeName": "OpenID Connect", - "openidScopeDescription": "Allows the app to access your OpenID Connect information.", - "emailScopeName": "Email", - "emailScopeDescription": "Allows the app to access your email address.", - "profileScopeName": "Profile", - "profileScopeDescription": "Allows the app to access your profile information.", - "groupsScopeName": "Groups", - "groupsScopeDescription": "Allows the app to access your group information.", - "backToLoginButton": "Back to login", - "phoneScopeName": "Phone", - "phoneScopeDescription": "Allows the app to access your phone number.", - "addressScopeName": "Address", - "addressScopeDescription": "Allows the app to access your address.", - "loginTailscaleTitle": "Continue with Tailscale", - "loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?", - "loginTailscaleDeviceName": "Device name:", - "loginTailscaleSubmit": "Continue with Tailscale", - "loginTailscaleOtherMethod": "Login with another method", - "loginTailscaleSuccess": "Successfully authenticated with Tailscale.", - "loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.", - "logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device {{deviceName}}. Click the button below to logout." + "loginTitle": "Welcome back, login with", + "loginTitleSimple": "Welcome back, please login", + "loginDivider": "Or", + "loginUsername": "Username", + "loginPassword": "Password", + "loginSubmit": "Login", + "loginFailTitle": "Failed to log in", + "loginFailSubtitle": "Please check your username and password", + "loginFailRateLimit": "You failed to login too many times. Please try again later", + "loginSuccessTitle": "Logged in", + "loginSuccessSubtitle": "Welcome back!", + "loginOauthFailTitle": "An error occurred", + "loginOauthFailSubtitle": "Failed to get OAuth URL", + "loginOauthSuccessTitle": "Redirecting", + "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", + "loginOauthAutoRedirectTitle": "OAuth Auto Redirect", + "loginOauthAutoRedirectSubtitle": "You will be automatically redirected to your OAuth provider to authenticate.", + "loginOauthAutoRedirectButton": "Redirect now", + "continueTitle": "Continue", + "continueRedirectingTitle": "Redirecting...", + "continueRedirectingSubtitle": "You should be redirected to the app soon", + "continueRedirectManually": "Redirect me manually", + "continueInsecureRedirectTitle": "Insecure redirect", + "continueInsecureRedirectSubtitle": "You are trying to redirect from https to http which is not secure. Are you sure you want to continue?", + "continueUntrustedRedirectTitle": "Untrusted redirect", + "continueUntrustedRedirectSubtitle": "You are trying to redirect to a domain that does not match your configured domain ({{cookieDomain}}). Are you sure you want to continue?", + "logoutFailTitle": "Failed to log out", + "logoutFailSubtitle": "Please try again", + "logoutSuccessTitle": "Logged out", + "logoutSuccessSubtitle": "You have been logged out", + "logoutTitle": "Logout", + "logoutUsernameSubtitle": "You are currently logged in as {{username}}. Click the button below to logout.", + "logoutOauthSubtitle": "You are currently logged in as {{username}} using the {{provider}} OAuth provider. Click the button below to logout.", + "notFoundTitle": "Page not found", + "notFoundSubtitle": "The page you are looking for does not exist.", + "notFoundButton": "Go home", + "totpFailTitle": "Failed to verify code", + "totpFailSubtitle": "Please check your code and try again", + "totpSuccessTitle": "Verified", + "totpSuccessSubtitle": "Redirecting to your app", + "totpTitle": "Enter your TOTP code", + "totpSubtitle": "Please enter the code from your authenticator app.", + "unauthorizedTitle": "Unauthorized", + "unauthorizedResourceSubtitle": "The user with username {{username}} is not authorized to access the resource {{resource}}.", + "unauthorizedLoginSubtitle": "The user with username {{username}} is not authorized to login.", + "unauthorizedGroupsSubtitle": "The user with username {{username}} is not in the groups required by the resource {{resource}}.", + "unauthorizedIpSubtitle": "Your IP address {{ip}} is not authorized to access the resource {{resource}}.", + "unauthorizedButton": "Try again", + "cancelTitle": "Cancel", + "forgotPasswordTitle": "Forgot your password?", + "failedToFetchProvidersTitle": "Failed to load authentication providers. Please check your configuration.", + "errorTitle": "An error occurred", + "errorSubtitleInfo": "The following error occurred while processing your request:", + "errorSubtitle": "An error occurred while trying to perform this action. Please check your browser console or the app logs for more information.", + "forgotPasswordMessage": "You can reset your password by changing the `USERS` environment variable.", + "fieldRequired": "This field is required", + "invalidInput": "Invalid input", + "domainWarningTitle": "Invalid Domain", + "domainWarningSubtitle": "You are accessing this instance from an incorrect domain. If you proceed, you may encounter issues with authentication.", + "domainWarningCurrent": "Current:", + "domainWarningExpected": "Expected:", + "ignoreTitle": "Ignore", + "goToCorrectDomainTitle": "Go to correct domain", + "authorizeTitle": "Authorize", + "authorizeCardTitle": "Continue to {{app}}?", + "authorizeSubtitle": "Would you like to continue to this app? Please carefully review the permissions requested by the app.", + "authorizeSubtitleOAuth": "Would you like to continue to this app?", + "authorizeLoadingTitle": "Loading...", + "authorizeLoadingSubtitle": "Please wait while we load the client information.", + "authorizeSuccessTitle": "Authorized", + "authorizeSuccessSubtitle": "You will be redirected to the app in a few seconds.", + "authorizeErrorClientInfo": "An error occurred while loading the client information. Please try again later.", + "authorizeErrorMissingParams": "The following parameters are missing: {{missingParams}}", + "openidScopeName": "OpenID Connect", + "openidScopeDescription": "Allows the app to access your OpenID Connect information.", + "emailScopeName": "Email", + "emailScopeDescription": "Allows the app to access your email address.", + "profileScopeName": "Profile", + "profileScopeDescription": "Allows the app to access your profile information.", + "groupsScopeName": "Groups", + "groupsScopeDescription": "Allows the app to access your group information.", + "backToLoginButton": "Back to login", + "phoneScopeName": "Phone", + "phoneScopeDescription": "Allows the app to access your phone number.", + "addressScopeName": "Address", + "addressScopeDescription": "Allows the app to access your address.", + "loginTailscaleTitle": "Continue with Tailscale", + "loginTailscaleDescription": "You appear to be accessing Tinyauth from an authorized Tailscale device. Would you like to continue with your Tailscale connection?", + "loginTailscaleDeviceName": "Device name:", + "loginTailscaleSubmit": "Continue with Tailscale", + "loginTailscaleOtherMethod": "Login with another method", + "loginTailscaleSuccess": "Successfully authenticated with Tailscale.", + "loginTailscaleFail": "Failed to authenticate with Tailscale. Please try again or use another login method.", + "logoutTailscaleSubtitle": "You are currently logged in with Tailscale on your device {{deviceName}}. Click the button below to logout.", + "quickActionsLanguage": "Language", + "quickActionsTheme": "Theme", + "quickActionsThemeLight": "Light", + "quickActionsThemeDark": "Dark", + "quickActionsThemeSystem": "System", + "quickActionsLogout": "Logout" } diff --git a/frontend/src/pages/authorize-page.tsx b/frontend/src/pages/authorize-page.tsx index 7f5c516c..1b7992c0 100644 --- a/frontend/src/pages/authorize-page.tsx +++ b/frontend/src/pages/authorize-page.tsx @@ -180,7 +180,7 @@ export const AuthorizePage = () => { {t("authorizeTitle")}