Compare commits

..

5 Commits

Author SHA1 Message Date
Stavros
f882fe9ec8 chore: update config description 2026-03-02 19:46:17 +02:00
Stavros
4caa6bf8ec refactor: categorize leftover config options 2026-03-02 19:32:25 +02:00
Stavros
c48181a8d8 refactor: use lowercase translated placeholders in login form 2026-03-02 15:58:52 +02:00
Stavros
06a26e976b fix: fix authorize app initial box not being a box 2026-03-02 15:50:08 +02:00
Stavros
27d14d6b21 New Crowdin updates (#676)
* New translations en.json (Romanian)

* New translations en.json (French)

* New translations en.json (Spanish)

* New translations en.json (Afrikaans)

* New translations en.json (Arabic)

* New translations en.json (Catalan)

* New translations en.json (Czech)

* New translations en.json (Danish)

* New translations en.json (German)

* New translations en.json (Greek)

* New translations en.json (Finnish)

* New translations en.json (Hebrew)

* New translations en.json (Hungarian)

* New translations en.json (Italian)

* New translations en.json (Japanese)

* New translations en.json (Korean)

* New translations en.json (Dutch)

* New translations en.json (Norwegian)

* New translations en.json (Polish)

* New translations en.json (Portuguese)

* New translations en.json (Russian)

* New translations en.json (Serbian (Cyrillic))

* New translations en.json (Swedish)

* New translations en.json (Turkish)

* New translations en.json (Ukrainian)

* New translations en.json (Chinese Simplified)

* New translations en.json (Chinese Traditional)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)
2026-03-01 13:56:21 +02:00
44 changed files with 173 additions and 107 deletions

View File

@@ -4,14 +4,23 @@
# The base URL where the app is hosted.
TINYAUTH_APPURL=
# The directory where resources are stored.
TINYAUTH_RESOURCESDIR="./resources"
# database config
# The path to the database file.
TINYAUTH_DATABASEPATH="./tinyauth.db"
# Disable analytics.
TINYAUTH_DISABLEANALYTICS=false
# Disable resources server.
TINYAUTH_DISABLERESOURCES=false
TINYAUTH_DATABASE_PATH="./tinyauth.db"
# analytics config
# Enable periodic version information collection.
TINYAUTH_ANALYTICS_ENABLED=true
# resources config
# Enable the resources server.
TINYAUTH_RESOURCES_ENABLED=true
# The directory where resources are stored.
TINYAUTH_RESOURCES_PATH="./resources"
# server config
@@ -130,8 +139,8 @@ TINYAUTH_UI_TITLE="Tinyauth"
TINYAUTH_UI_FORGOTPASSWORDMESSAGE="You can change your password by changing the configuration."
# Path to the background image.
TINYAUTH_UI_BACKGROUNDIMAGE="/background.jpg"
# Disable UI warnings.
TINYAUTH_UI_DISABLEWARNINGS=false
# Enable UI warnings.
TINYAUTH_UI_WARNINGSENABLED=true
# ldap config

View File

@@ -43,7 +43,7 @@ export const LoginForm = (props: Props) => {
<FormLabel className="mb-2">{t("loginUsername")}</FormLabel>
<FormControl className="mb-1">
<Input
placeholder={t("loginUsername")}
placeholder={t("loginUsername").toLocaleLowerCase()}
disabled={loading}
autoComplete="username"
{...field}
@@ -62,7 +62,7 @@ export const LoginForm = (props: Props) => {
<FormLabel className="mb-2">{t("loginPassword")}</FormLabel>
<FormControl>
<Input
placeholder={t("loginPassword")}
placeholder={t("loginPassword").toLowerCase()}
type="password"
disabled={loading}
autoComplete="current-password"

View File

@@ -31,7 +31,7 @@ const BaseLayout = ({ children }: { children: React.ReactNode }) => {
};
export const Layout = () => {
const { appUrl, disableUiWarnings } = useAppContext();
const { appUrl, warningsEnabled } = useAppContext();
const [ignoreDomainWarning, setIgnoreDomainWarning] = useState(() => {
return window.sessionStorage.getItem("ignoreDomainWarning") === "true";
});
@@ -42,7 +42,7 @@ export const Layout = () => {
setIgnoreDomainWarning(true);
}, [setIgnoreDomainWarning]);
if (!ignoreDomainWarning && !disableUiWarnings && appUrl !== currentUrl) {
if (!ignoreDomainWarning && warningsEnabled && appUrl !== currentUrl) {
return (
<BaseLayout>
<DomainWarning

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "تجاهل",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Neplatný údaj",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Ungültige Eingabe",
"domainWarningTitle": "Ungültige Domain",
"domainWarningSubtitle": "Diese Instanz ist so konfiguriert, dass sie von <code>{{appUrl}}</code> aufgerufen werden kann, aber <code>{{currentUrl}}</code> wird verwendet. Wenn Sie fortfahren, können Probleme bei der Authentifizierung auftreten.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignorieren",
"goToCorrectDomainTitle": "Zur korrekten Domain gehen",
"authorizeTitle": "Authorize",

View File

@@ -1,9 +1,9 @@
{
"loginTitle": "Καλώς ήρθατε, συνδεθείτε με",
"loginTitleSimple": "Καλώς ορίσατε, παρακαλώ συνδεθείτε",
"loginTitleSimple": "Καλώς ήρθατε, παρακαλώ συνδεθείτε",
"loginDivider": "Ή",
"loginUsername": "Όνομα Χρήστη",
"loginPassword": "Κωδικός",
"loginPassword": "Κωδικόs πρόσβασης",
"loginSubmit": "Είσοδος",
"loginFailTitle": "Αποτυχία σύνδεσης",
"loginFailSubtitle": "Παρακαλώ ελέγξτε το όνομα χρήστη και τον κωδικό πρόσβασης",
@@ -19,7 +19,7 @@
"loginOauthAutoRedirectButton": "Ανακατεύθυνση τώρα",
"continueTitle": "Συνέχεια",
"continueRedirectingTitle": "Ανακατεύθυνση...",
"continueRedirectingSubtitle": "Θα πρέπει να μεταφερθείτε σύντομα στην εφαρμογή σας",
"continueRedirectingSubtitle": "Θα μεταφερθείτε σύντομα στην εφαρμογή σας",
"continueRedirectManually": "Χειροκίνητη ανακατεύθυνση",
"continueInsecureRedirectTitle": "Μη ασφαλής ανακατεύθυνση",
"continueInsecureRedirectSubtitle": "Προσπαθείτε να ανακατευθύνετε από <code>https</code> σε <code>http</code> το οποίο δεν είναι ασφαλές. Είστε σίγουροι ότι θέλετε να συνεχίσετε;",
@@ -41,7 +41,7 @@
"totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας",
"totpTitle": "Εισάγετε τον κωδικό TOTP",
"totpSubtitle": "Παρακαλώ εισάγετε τον κωδικό από την εφαρμογή ελέγχου ταυτότητας.",
"unauthorizedTitle": "Μη εξουσιοδοτημένο",
"unauthorizedTitle": "Σφάλμα μη εξουσιοδότησης",
"unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν έχει άδεια πρόσβασης στον πόρο <code>{{resource}}</code>.",
"unauthorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν είναι εξουσιοδοτημένος να συνδεθεί.",
"unauthorizedGroupsSubtitle": "Ο χρήστης με όνομα χρήστη <code>{{username}}</code> δεν είναι στις ομάδες που απαιτούνται από τον πόρο <code>{{resource}}</code>.",
@@ -57,7 +57,9 @@
"fieldRequired": "Αυτό το πεδίο είναι υποχρεωτικό",
"invalidInput": "Μη έγκυρη καταχώρηση",
"domainWarningTitle": "Μη έγκυρο domain",
"domainWarningSubtitle": "Αυτή η εφαρμογή έχει ρυθμιστεί για πρόσβαση από <code>{{appUrl}}</code>, αλλά <code>{{currentUrl}}</code> χρησιμοποιείται. Αν συνεχίσετε, μπορεί να αντιμετωπίσετε προβλήματα με την ταυτοποίηση.",
"domainWarningSubtitle": "Έχετε επισκεφθεί αυτή την εφαρμογή από λανθασμένο domain. Αν προχωρήσετε, ενδέχεται να αντιμετωπίσετε προβλήματα με τον έλεγχο ταυτότητας.",
"domainWarningCurrent": "Τρέχον:",
"domainWarningExpected": "Αναμένεται:",
"ignoreTitle": "Παράβλεψη",
"goToCorrectDomainTitle": "Μεταβείτε στο σωστό domain",
"authorizeTitle": "Εξουσιοδότηση",
@@ -72,7 +74,7 @@
"authorizeErrorMissingParams": "Οι παρακάτω απαραίτητες πληροφορίες λείπουν από το αίτημά σας: {{missingParams}}",
"openidScopeName": "Σύνδεση OpenID",
"openidScopeDescription": "Επιτρέπει στην εφαρμογή την πρόσβαση στις πληροφορίες σύνδεσης OpenID.",
"emailScopeName": "Διεύθυνση ηλεκτρονικού ταχυδρομείου",
"emailScopeName": "Ηλεκτρονικό ταχυδρομείο",
"emailScopeDescription": "Επιτρέπει στην εφαρμογή να έχει πρόσβαση στη διεύθυνση ηλεκτρονικού ταχυδρομείου σας.",
"profileScopeName": "Προφίλ",
"profileScopeDescription": "Επιτρέπει στην εφαρμογή να έχει πρόσβαση στις πληροφορίες του προφίλ σας.",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Virheellinen syöte",
"domainWarningTitle": "Virheellinen verkkotunnus",
"domainWarningSubtitle": "Tämä instanssi on määritelty käyttämään osoitetta <code>{{appUrl}}</code>, mutta nykyinen osoite on <code>{{currentUrl}}</code>. Jos jatkat, saatat törmätä ongelmiin autentikoinnissa.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Jätä huomiotta",
"goToCorrectDomainTitle": "Siirry oikeaan verkkotunnukseen",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Saisie non valide",
"domainWarningTitle": "Domaine invalide",
"domainWarningSubtitle": "Cette instance est configurée pour être accédée depuis <code>{{appUrl}}</code>, mais <code>{{currentUrl}}</code> est utilisé. Si vous continuez, vous pourriez rencontrer des problèmes d'authentification.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignorer",
"goToCorrectDomainTitle": "Aller au bon domaine",
"authorizeTitle": "Autoriser",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Input non valido",
"domainWarningTitle": "Dominio non valido",
"domainWarningSubtitle": "Questa istanza è configurata per essere accessibile da <code>{{appUrl}}</code>, ma la stai visitando da <code>{{currentUrl}}</code>. Se procedi, potresti incorrere in problemi di autenticazione.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignora",
"goToCorrectDomainTitle": "Vai al dominio corretto",
"authorizeTitle": "Autorizza",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Ongeldige invoer",
"domainWarningTitle": "Ongeldig domein",
"domainWarningSubtitle": "Deze instantie is geconfigureerd voor toegang tot <code>{{appUrl}}</code>, maar <code>{{currentUrl}}</code> wordt gebruikt. Als je doorgaat, kun je problemen ondervinden met authenticatie.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Negeren",
"goToCorrectDomainTitle": "Ga naar het juiste domein",
"authorizeTitle": "Autoriseren",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Nieprawidłowe dane wejściowe",
"domainWarningTitle": "Nieprawidłowa domena",
"domainWarningSubtitle": "Ta instancja jest skonfigurowana do uzyskania dostępu z <code>{{appUrl}}</code>, ale <code>{{currentUrl}}</code> jest w użyciu. Jeśli będziesz kontynuować, mogą wystąpić problemy z uwierzytelnianiem.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Zignoruj",
"goToCorrectDomainTitle": "Przejdź do prawidłowej domeny",
"authorizeTitle": "Autoryzuj",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Entrada Inválida",
"domainWarningTitle": "Domínio inválido",
"domainWarningSubtitle": "Esta instância está configurada para ser acessada de <code>{{appUrl}}</code>, mas <code>{{currentUrl}}</code> está sendo usado. Se você continuar, você pode encontrar problemas com a autenticação.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignorar",
"goToCorrectDomainTitle": "Ir para o domínio correto",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Entrada inválida",
"domainWarningTitle": "Domínio inválido",
"domainWarningSubtitle": "Esta instância está configurada para ser acedida a partir de <code>{{appUrl}}</code>, mas está a ser usado <code>{{currentUrl}}</code>. Se continuares, poderás ter problemas de autenticação.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignorar",
"goToCorrectDomainTitle": "Ir para o domínio correto",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Недопустимый ввод",
"domainWarningTitle": "Неверный домен",
"domainWarningSubtitle": "Этот экземпляр настроен на доступ к нему из <code>{{appUrl}}</code>, но <code>{{currentUrl}}</code> в настоящее время используется. Если вы продолжите, то могут возникнуть проблемы с авторизацией.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Игнорировать",
"goToCorrectDomainTitle": "Перейти к правильному домену",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Неисправан унос",
"domainWarningTitle": "Неисправан домен",
"domainWarningSubtitle": "Ова инстанца је подешена да јој се приступа са <code>{{appUrl}}</code>, али се користи <code>{{currentUrl}}</code>. Ако наставите, можете искусити проблеме са аутентификацијом.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Игнориши",
"goToCorrectDomainTitle": "Иди на исправан домен",
"authorizeTitle": "Ауторизуј",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Geçersiz girdi",
"domainWarningTitle": "Geçersiz alan adı",
"domainWarningSubtitle": "Bu örnek, <code>{{appUrl}}</code> adresinden erişilecek şekilde yapılandırılmıştır, ancak <code>{{currentUrl}}</code> kullanılmaktadır. Devam ederseniz, kimlik doğrulama ile ilgili sorunlarla karşılaşabilirsiniz.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Yoksay",
"goToCorrectDomainTitle": "Doğru alana gidin",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Невірне введення",
"domainWarningTitle": "Невірний домен",
"domainWarningSubtitle": "Даний ресурс налаштований для доступу з <code>{{appUrl}}</code>, але використовується <code>{{currentUrl}}</code>. Якщо ви продовжите, можуть виникнути проблеми з автентифікацією.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ігнорувати",
"goToCorrectDomainTitle": "Перейти за коректним доменом",
"authorizeTitle": "Авторизуватись",

View File

@@ -58,6 +58,8 @@
"invalidInput": "Invalid input",
"domainWarningTitle": "Invalid Domain",
"domainWarningSubtitle": "This instance is configured to be accessed from <code>{{appUrl}}</code>, but <code>{{currentUrl}}</code> is being used. If you proceed, you may encounter issues with authentication.",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "Ignore",
"goToCorrectDomainTitle": "Go to correct domain",
"authorizeTitle": "Authorize",

View File

@@ -58,6 +58,8 @@
"invalidInput": "无效的输入",
"domainWarningTitle": "无效域名",
"domainWarningSubtitle": "当前实例配置的访问地址为 <code>{{appUrl}}</code>,但您正在使用 <code>{{currentUrl}}</code>。若继续操作,可能会遇到身份验证问题。",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "忽略",
"goToCorrectDomainTitle": "转到正确的域名",
"authorizeTitle": "授权",

View File

@@ -58,6 +58,8 @@
"invalidInput": "無效的輸入",
"domainWarningTitle": "無效的網域",
"domainWarningSubtitle": "此服務設定為透過 <code>{{appUrl}}</code> 存取,但目前使用的是 <code>{{currentUrl}}</code>。若繼續操作,可能會遇到驗證問題。",
"domainWarningCurrent": "Current:",
"domainWarningExpected": "Expected:",
"ignoreTitle": "忽略",
"goToCorrectDomainTitle": "前往正確域名",
"authorizeTitle": "授權",

View File

@@ -155,7 +155,7 @@ export const AuthorizePage = () => {
<Card>
<CardHeader className="mb-2">
<div className="flex flex-col gap-3 items-center justify-center text-center">
<div className="bg-accent-foreground text-muted text-xl font-bold font-sans rounded-lg px-4 py-3">
<div className="bg-accent-foreground box-content text-muted text-xl font-bold font-sans rounded-lg size-10 p-2 flex items-center justify-center">
{getClientInfo.data?.name.slice(0, 1)}
</div>
<CardTitle className="text-xl">
@@ -170,8 +170,8 @@ export const AuthorizePage = () => {
</CardDescription>
</div>
</CardHeader>
<CardContent className="mb-2">
{scopes.includes("openid") && (
{scopes.includes("openid") && (
<CardContent className="mb-2">
<div className="flex flex-wrap gap-2 items-center justify-center">
{scopes.map((id) => {
const scope = scopeMap.find((s) => s.id === id);
@@ -189,8 +189,8 @@ export const AuthorizePage = () => {
);
})}
</div>
)}
</CardContent>
</CardContent>
)}
<CardFooter className="flex flex-col items-stretch gap-3">
<Button
onClick={() => authorizeMutation.mutate()}

View File

@@ -14,7 +14,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { useRedirectUri } from "@/lib/hooks/redirect-uri";
export const ContinuePage = () => {
const { cookieDomain, disableUiWarnings } = useAppContext();
const { cookieDomain, warningsEnabled } = useAppContext();
const { isLoggedIn } = useUserContext();
const { search } = useLocation();
const { t } = useTranslation();
@@ -35,10 +35,9 @@ export const ContinuePage = () => {
const urlHref = url?.href;
const hasValidRedirect = valid && allowedProto;
const showUntrustedWarning =
hasValidRedirect && !trusted && !disableUiWarnings;
const showUntrustedWarning = hasValidRedirect && !trusted && warningsEnabled;
const showInsecureWarning =
hasValidRedirect && httpsDowngrade && !disableUiWarnings;
hasValidRedirect && httpsDowngrade && warningsEnabled;
const shouldAutoRedirect =
isLoggedIn &&
hasValidRedirect &&

View File

@@ -258,13 +258,13 @@ export const LoginPage = () => {
/>
)}
{providers.length == 0 && (
<p className="text-center text-red-600 max-w-sm">
<pre className="break-normal! text-sm text-red-600">
{t("failedToFetchProvidersTitle")}
</p>
</pre>
)}
</CardContent>
<CardFooter>
{userAuthConfigured && (
{userAuthConfigured && (
<CardFooter>
<Button
className="w-full"
type="submit"
@@ -273,8 +273,8 @@ export const LoginPage = () => {
>
{t("loginSubmit")}
</Button>
)}
</CardFooter>
</CardFooter>
)}
</Card>
);
};

View File

@@ -14,7 +14,7 @@ export const appContextSchema = z.object({
forgotPasswordMessage: z.string(),
backgroundImage: z.string(),
oauthAutoRedirect: z.string(),
disableUiWarnings: z.boolean(),
warningsEnabled: z.boolean(),
});
export type AppContextSchema = z.infer<typeof appContextSchema>;

View File

@@ -124,7 +124,7 @@ func (app *BootstrapApp) Setup() error {
tlog.App.Trace().Str("redirectCookieName", app.context.redirectCookieName).Msg("Redirect cookie name")
// Database
db, err := app.SetupDatabase(app.config.DatabasePath)
db, err := app.SetupDatabase(app.config.Database.Path)
if err != nil {
return fmt.Errorf("failed to setup database: %w", err)
@@ -193,7 +193,7 @@ func (app *BootstrapApp) Setup() error {
go app.dbCleanup(queries)
// If analytics are not disabled, start heartbeat
if !app.config.DisableAnalytics {
if app.config.Analytics.Enabled {
tlog.App.Debug().Msg("Starting heartbeat routine")
go app.heartbeat()
}

View File

@@ -71,7 +71,7 @@ func (app *BootstrapApp) setupRouter() (*gin.Engine, error) {
ForgotPasswordMessage: app.config.UI.ForgotPasswordMessage,
BackgroundImage: app.config.UI.BackgroundImage,
OAuthAutoRedirect: app.config.OAuth.AutoRedirect,
DisableUIWarnings: app.config.UI.DisableWarnings,
WarningsEnabled: app.config.UI.WarningsEnabled,
}, apiRouter)
contextController.SetupRoutes()
@@ -103,8 +103,8 @@ func (app *BootstrapApp) setupRouter() (*gin.Engine, error) {
userController.SetupRoutes()
resourcesController := controller.NewResourcesController(controller.ResourcesControllerConfig{
ResourcesDir: app.config.ResourcesDir,
ResourcesDisabled: app.config.DisableResources,
Path: app.config.Resources.Path,
Enabled: app.config.Resources.Enabled,
}, &engine.RouterGroup)
resourcesController.SetupRoutes()

View File

@@ -3,8 +3,16 @@ package config
// Default configuration
func NewDefaultConfiguration() *Config {
return &Config{
ResourcesDir: "./resources",
DatabasePath: "./tinyauth.db",
Database: DatabaseConfig{
Path: "./tinyauth.db",
},
Analytics: AnalyticsConfig{
Enabled: true,
},
Resources: ResourcesConfig{
Enabled: true,
Path: "./resources",
},
Server: ServerConfig{
Port: 3000,
Address: "0.0.0.0",
@@ -19,6 +27,7 @@ func NewDefaultConfiguration() *Config {
Title: "Tinyauth",
ForgotPasswordMessage: "You can change your password by changing the configuration.",
BackgroundImage: "/background.jpg",
WarningsEnabled: true,
},
Ldap: LdapConfig{
Insecure: false,
@@ -68,20 +77,32 @@ var RedirectCookieName = "tinyauth-redirect"
// Main app config
type Config struct {
AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"`
ResourcesDir string `description:"The directory where resources are stored." yaml:"resourcesDir"`
DatabasePath string `description:"The path to the database file." yaml:"databasePath"`
DisableAnalytics bool `description:"Disable analytics." yaml:"disableAnalytics"`
DisableResources bool `description:"Disable resources server." yaml:"disableResources"`
Server ServerConfig `description:"Server configuration." yaml:"server"`
Auth AuthConfig `description:"Authentication configuration." yaml:"auth"`
Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"`
OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"`
OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"`
UI UIConfig `description:"UI customization." yaml:"ui"`
Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"`
Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"`
Log LogConfig `description:"Logging configuration." yaml:"log"`
AppURL string `description:"The base URL where the app is hosted." yaml:"appUrl"`
Database DatabaseConfig `description:"Database configuration." yaml:"database"`
Analytics AnalyticsConfig `description:"Analytics configuration." yaml:"analytics"`
Resources ResourcesConfig `description:"Resources configuration." yaml:"resources"`
Server ServerConfig `description:"Server configuration." yaml:"server"`
Auth AuthConfig `description:"Authentication configuration." yaml:"auth"`
Apps map[string]App `description:"Application ACLs configuration." yaml:"apps"`
OAuth OAuthConfig `description:"OAuth configuration." yaml:"oauth"`
OIDC OIDCConfig `description:"OIDC configuration." yaml:"oidc"`
UI UIConfig `description:"UI customization." yaml:"ui"`
Ldap LdapConfig `description:"LDAP configuration." yaml:"ldap"`
Experimental ExperimentalConfig `description:"Experimental features, use with caution." yaml:"experimental"`
Log LogConfig `description:"Logging configuration." yaml:"log"`
}
type DatabaseConfig struct {
Path string `description:"The path to the database, including file name." yaml:"path"`
}
type AnalyticsConfig struct {
Enabled bool `description:"Enable periodic version information collection." yaml:"enabled"`
}
type ResourcesConfig struct {
Enabled bool `description:"Enable the resources server." yaml:"enabled"`
Path string `description:"The directory where resources are stored." yaml:"path"`
}
type ServerConfig struct {
@@ -114,8 +135,8 @@ type OAuthConfig struct {
}
type OIDCConfig struct {
PrivateKeyPath string `description:"Path to the private key file." yaml:"privateKeyPath"`
PublicKeyPath string `description:"Path to the public key file." yaml:"publicKeyPath"`
PrivateKeyPath string `description:"Path to the private key file, including file name." yaml:"privateKeyPath"`
PublicKeyPath string `description:"Path to the public key file, including file name." yaml:"publicKeyPath"`
Clients map[string]OIDCClientConfig `description:"OIDC clients configuration." yaml:"clients"`
}
@@ -123,7 +144,7 @@ type UIConfig struct {
Title string `description:"The title of the UI." yaml:"title"`
ForgotPasswordMessage string `description:"Message displayed on the forgot password page." yaml:"forgotPasswordMessage"`
BackgroundImage string `description:"Path to the background image." yaml:"backgroundImage"`
DisableWarnings bool `description:"Disable UI warnings." yaml:"disableWarnings"`
WarningsEnabled bool `description:"Enable UI warnings." yaml:"warningsEnabled"`
}
type LdapConfig struct {

View File

@@ -33,7 +33,7 @@ type AppContextResponse struct {
ForgotPasswordMessage string `json:"forgotPasswordMessage"`
BackgroundImage string `json:"backgroundImage"`
OAuthAutoRedirect string `json:"oauthAutoRedirect"`
DisableUIWarnings bool `json:"disableUiWarnings"`
WarningsEnabled bool `json:"warningsEnabled"`
}
type Provider struct {
@@ -50,7 +50,7 @@ type ContextControllerConfig struct {
ForgotPasswordMessage string
BackgroundImage string
OAuthAutoRedirect string
DisableUIWarnings bool
WarningsEnabled bool
}
type ContextController struct {
@@ -59,7 +59,7 @@ type ContextController struct {
}
func NewContextController(config ContextControllerConfig, router *gin.RouterGroup) *ContextController {
if config.DisableUIWarnings {
if !config.WarningsEnabled {
tlog.App.Warn().Msg("UI warnings are disabled. This may expose users to security risks. Proceed with caution.")
}
@@ -124,6 +124,6 @@ func (controller *ContextController) appContextHandler(c *gin.Context) {
ForgotPasswordMessage: controller.config.ForgotPasswordMessage,
BackgroundImage: controller.config.BackgroundImage,
OAuthAutoRedirect: controller.config.OAuthAutoRedirect,
DisableUIWarnings: controller.config.DisableUIWarnings,
WarningsEnabled: controller.config.WarningsEnabled,
})
}

View File

@@ -32,7 +32,7 @@ var contextControllerCfg = controller.ContextControllerConfig{
ForgotPasswordMessage: "Contact admin to reset your password.",
BackgroundImage: "/assets/bg.jpg",
OAuthAutoRedirect: "google",
DisableUIWarnings: false,
WarningsEnabled: true,
}
var contextCtrlTestContext = config.UserContext{
@@ -82,7 +82,7 @@ func TestAppContextHandler(t *testing.T) {
ForgotPasswordMessage: contextControllerCfg.ForgotPasswordMessage,
BackgroundImage: contextControllerCfg.BackgroundImage,
OAuthAutoRedirect: contextControllerCfg.OAuthAutoRedirect,
DisableUIWarnings: contextControllerCfg.DisableUIWarnings,
WarningsEnabled: contextControllerCfg.WarningsEnabled,
}
router, recorder := setupContextController(nil)

View File

@@ -7,8 +7,8 @@ import (
)
type ResourcesControllerConfig struct {
ResourcesDir string
ResourcesDisabled bool
Path string
Enabled bool
}
type ResourcesController struct {
@@ -18,7 +18,7 @@ type ResourcesController struct {
}
func NewResourcesController(config ResourcesControllerConfig, router *gin.RouterGroup) *ResourcesController {
fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(config.ResourcesDir)))
fileServer := http.StripPrefix("/resources", http.FileServer(http.Dir(config.Path)))
return &ResourcesController{
config: config,
@@ -32,14 +32,14 @@ func (controller *ResourcesController) SetupRoutes() {
}
func (controller *ResourcesController) resourcesHandler(c *gin.Context) {
if controller.config.ResourcesDir == "" {
if controller.config.Path == "" {
c.JSON(404, gin.H{
"status": 404,
"message": "Resources not found",
})
return
}
if controller.config.ResourcesDisabled {
if !controller.config.Enabled {
c.JSON(403, gin.H{
"status": 403,
"message": "Resources are disabled",

View File

@@ -18,7 +18,8 @@ func TestResourcesHandler(t *testing.T) {
group := router.Group("/")
ctrl := controller.NewResourcesController(controller.ResourcesControllerConfig{
ResourcesDir: "/tmp/tinyauth",
Path: "/tmp/tinyauth",
Enabled: true,
}, group)
ctrl.SetupRoutes()

View File

@@ -41,15 +41,11 @@ var (
)
type ClaimSet struct {
Iss string `json:"iss"`
Aud string `json:"aud"`
Sub string `json:"sub"`
Iat int64 `json:"iat"`
Exp int64 `json:"exp"`
Name string `json:"name,omitempty"`
Email string `json:"email,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
Groups []string `json:"groups,omitempty"`
Iss string `json:"iss"`
Aud string `json:"aud"`
Sub string `json:"sub"`
Iat int64 `json:"iat"`
Exp int64 `json:"exp"`
}
type UserinfoResponse struct {
@@ -57,7 +53,7 @@ type UserinfoResponse struct {
Name string `json:"name"`
Email string `json:"email"`
PreferredUsername string `json:"preferred_username"`
Groups []string `json:"groups,omitempty"`
Groups []string `json:"groups"`
UpdatedAt int64 `json:"updated_at"`
}
@@ -353,7 +349,7 @@ func (service *OIDCService) GetCodeEntry(c *gin.Context, codeHash string) (repos
return oidcCode, nil
}
func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, user repository.OidcUserinfo, scope string) (string, error) {
func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, sub string) (string, error) {
createdAt := time.Now().Unix()
expiresAt := time.Now().Add(time.Duration(service.config.SessionExpiry) * time.Second).Unix()
@@ -371,18 +367,12 @@ func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, user
return "", err
}
userInfo := service.CompileUserinfo(user, scope)
claims := ClaimSet{
Iss: service.issuer,
Aud: client.ClientID,
Sub: user.Sub,
Iat: createdAt,
Exp: expiresAt,
Name: userInfo.Name,
Email: userInfo.Email,
PreferredUsername: userInfo.PreferredUsername,
Groups: userInfo.Groups,
Iss: service.issuer,
Aud: client.ClientID,
Sub: sub,
Iat: createdAt,
Exp: expiresAt,
}
payload, err := json.Marshal(claims)
@@ -407,13 +397,7 @@ func (service *OIDCService) generateIDToken(client config.OIDCClientConfig, user
}
func (service *OIDCService) GenerateAccessToken(c *gin.Context, client config.OIDCClientConfig, sub string, scope string) (TokenResponse, error) {
user, err := service.GetUserinfo(c, sub)
if err != nil {
return TokenResponse{}, err
}
idToken, err := service.generateIDToken(client, user, scope)
idToken, err := service.generateIDToken(client, sub)
if err != nil {
return TokenResponse{}, err
@@ -472,15 +456,9 @@ func (service *OIDCService) RefreshAccessToken(c *gin.Context, refreshToken stri
return TokenResponse{}, ErrInvalidClient
}
user, err := service.GetUserinfo(c, entry.Sub)
if err != nil {
return TokenResponse{}, err
}
idToken, err := service.generateIDToken(config.OIDCClientConfig{
ClientID: entry.ClientID,
}, user, entry.Scope)
}, entry.Sub)
if err != nil {
return TokenResponse{}, err