Compare commits

...

15 Commits

Author SHA1 Message Date
Stavros
61dfc91110 chore: update screenshot 2025-04-10 15:52:07 +03:00
Stavros
130e6facb7 New Crowdin updates (#68)
* New translations en.json (Dutch)

* New translations en.json (German)

* New translations en.json (Arabic)

* New translations en.json (Arabic)
2025-04-10 15:44:05 +03:00
Stavros
525f4f3041 chore: bump version 2025-04-10 15:35:43 +03:00
Stavros
8a21345706 fix: handle new lines and spaces in the secret files 2025-04-10 15:34:46 +03:00
Stavros
1169c633cc refactor: remove tailscale oauth 2025-04-10 15:14:01 +03:00
Stavros
2242c9c1e6 chore: use light mode for screenshot 2025-04-09 20:25:31 +03:00
Stavros
939919df39 New Crowdin updates (#67)
* 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 (English)

* New translations en.json (Vietnamese)

* New translations en.json (Portuguese, Brazilian)

* New translations en.json (French)

* New translations en.json (Greek)

* New translations en.json (Polish)
2025-04-09 17:49:19 +03:00
Stavros
a579cf37ce fix: allow user if users label is empty 2025-04-09 17:44:59 +03:00
Stavros
2647aa07b4 fix: fix translations error 2025-04-09 17:38:57 +03:00
Stavros
f68c580e11 fix: fix tailscale icon color 2025-04-09 17:36:59 +03:00
Stavros
9b39a2b856 fix: use arm runner 2025-04-09 16:22:21 +03:00
Stavros
6d17ce699a fix: download binaries correctly 2025-04-09 16:18:51 +03:00
Stavros
20dbb35d44 chore: build amd64 and arm64 binaries 2025-04-09 16:12:25 +03:00
Stavros
36d9dd7354 New Crowdin updates (#64)
* New translations en.json (Dutch)

* New translations en.json (German)
2025-04-09 15:59:39 +03:00
Stavros
5129f9bff8 feat: light mode 2025-04-09 15:58:39 +03:00
49 changed files with 354 additions and 473 deletions

View File

@@ -12,9 +12,6 @@ GITHUB_CLIENT_SECRET_FILE=github_client_secret_file
GOOGLE_CLIENT_ID=google_client_id GOOGLE_CLIENT_ID=google_client_id
GOOGLE_CLIENT_SECRET=google_client_secret GOOGLE_CLIENT_SECRET=google_client_secret
GOOGLE_CLIENT_SECRET_FILE=google_client_secret_file GOOGLE_CLIENT_SECRET_FILE=google_client_secret_file
TAILSCALE_CLIENT_ID=tailscale_client_id
TAILSCALE_CLIENT_SECRET=tailscale_client_secret
TAILSCALE_CLIENT_SECRET_FILE=tailscale__client_secret_file
GENERIC_CLIENT_ID=generic_client_id GENERIC_CLIENT_ID=generic_client_id
GENERIC_CLIENT_SECRET=generic_client_secret GENERIC_CLIENT_SECRET=generic_client_secret
GENERIC_CLIENT_SECRET_FILE=generic_client_secret_file GENERIC_CLIENT_SECRET_FILE=generic_client_secret_file

View File

@@ -6,7 +6,85 @@ on:
- "v*" - "v*"
jobs: jobs:
build: binary-build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/setup-go@v5
with:
go-version: "^1.23.2"
- name: Install frontend dependencies
run: |
cd frontend
bun install
- name: Install backend dependencies
run: |
go mod tidy
- name: Build frontend
run: |
cd frontend
bun run build
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
CGO_ENABLED=0 go build -ldflags "-s -w" -o tinyauth-amd64
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: tinyauth-amd64
path: tinyauth-amd64
binary-build-arm:
runs-on: ubuntu-24.04-arm
steps:
- name: Checkout
uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- uses: actions/setup-go@v5
with:
go-version: "^1.23.2"
- name: Install frontend dependencies
run: |
cd frontend
bun install
- name: Install backend dependencies
run: |
go mod tidy
- name: Build frontend
run: |
cd frontend
bun run build
- name: Build
run: |
cp -r frontend/dist internal/assets/dist
CGO_ENABLED=0 go build -ldflags "-s -w" -o tinyauth-arm64
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: tinyauth-arm64
path: tinyauth-arm64
image-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
@@ -51,7 +129,7 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
build-arm: image-build-arm:
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
steps: steps:
- name: Checkout - name: Checkout
@@ -96,11 +174,11 @@ jobs:
if-no-files-found: error if-no-files-found: error
retention-days: 1 retention-days: 1
merge: image-merge:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: needs:
- build - image-build
- build-arm - image-build-arm
steps: steps:
- name: Download digests - name: Download digests
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
@@ -134,3 +212,20 @@ jobs:
run: | run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *) $(printf 'ghcr.io/${{ github.repository_owner }}/tinyauth@sha256:%s ' *)
update-release:
runs-on: ubuntu-latest
needs:
- binary-build
- binary-build-arm
steps:
- uses: actions/download-artifact@v4
with:
pattern: tinyauth-*
path: binaries
merge-multiple: true
- name: Release
uses: softprops/action-gh-release@v2
with:
files: binaries/*

View File

@@ -17,7 +17,7 @@
Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps. It is made for traefik but it can be extended to work with all reverse proxies like caddy and nginx. Tinyauth is a simple authentication middleware that adds simple username/password login or OAuth with Google, Github and any generic OAuth provider to all of your docker apps. It is made for traefik but it can be extended to work with all reverse proxies like caddy and nginx.
![Login](assets/login.png) ![Login](assets/screenshot.png)
> [!WARNING] > [!WARNING]
> Tinyauth is in active development and configuration may change often. Please make sure to carefully read the release notes before updating. > Tinyauth is in active development and configuration may change often. Please make sure to carefully read the release notes before updating.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

BIN
assets/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

View File

@@ -42,7 +42,6 @@ var rootCmd = &cobra.Command{
config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile)
config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile)
config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile)
config.TailscaleClientSecret = utils.GetSecret(config.TailscaleClientSecret, config.TailscaleClientSecretFile)
// Validate config // Validate config
validator := validator.New() validator := validator.New()
@@ -77,19 +76,17 @@ var rootCmd = &cobra.Command{
// Create OAuth config // Create OAuth config
oauthConfig := types.OAuthConfig{ oauthConfig := types.OAuthConfig{
GithubClientId: config.GithubClientId, GithubClientId: config.GithubClientId,
GithubClientSecret: config.GithubClientSecret, GithubClientSecret: config.GithubClientSecret,
GoogleClientId: config.GoogleClientId, GoogleClientId: config.GoogleClientId,
GoogleClientSecret: config.GoogleClientSecret, GoogleClientSecret: config.GoogleClientSecret,
TailscaleClientId: config.TailscaleClientId, GenericClientId: config.GenericClientId,
TailscaleClientSecret: config.TailscaleClientSecret, GenericClientSecret: config.GenericClientSecret,
GenericClientId: config.GenericClientId, GenericScopes: strings.Split(config.GenericScopes, ","),
GenericClientSecret: config.GenericClientSecret, GenericAuthURL: config.GenericAuthURL,
GenericScopes: strings.Split(config.GenericScopes, ","), GenericTokenURL: config.GenericTokenURL,
GenericAuthURL: config.GenericAuthURL, GenericUserURL: config.GenericUserURL,
GenericTokenURL: config.GenericTokenURL, AppURL: config.AppURL,
GenericUserURL: config.GenericUserURL,
AppURL: config.AppURL,
} }
// Create handlers config // Create handlers config
@@ -189,9 +186,6 @@ func init() {
rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.") rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.")
rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.") rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.")
rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.") rootCmd.Flags().String("google-client-secret-file", "", "Google OAuth client secret file.")
rootCmd.Flags().String("tailscale-client-id", "", "Tailscale OAuth client ID.")
rootCmd.Flags().String("tailscale-client-secret", "", "Tailscale OAuth client secret.")
rootCmd.Flags().String("tailscale-client-secret-file", "", "Tailscale OAuth client secret file.")
rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.") rootCmd.Flags().String("generic-client-id", "", "Generic OAuth client ID.")
rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.") rootCmd.Flags().String("generic-client-secret", "", "Generic OAuth client secret.")
rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.") rootCmd.Flags().String("generic-client-secret-file", "", "Generic OAuth client secret file.")
@@ -223,9 +217,6 @@ func init() {
viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID") viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID")
viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET") viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET")
viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE") viper.BindEnv("google-client-secret-file", "GOOGLE_CLIENT_SECRET_FILE")
viper.BindEnv("tailscale-client-id", "TAILSCALE_CLIENT_ID")
viper.BindEnv("tailscale-client-secret", "TAILSCALE_CLIENT_SECRET")
viper.BindEnv("tailscale-client-secret-file", "TAILSCALE_CLIENT_SECRET_FILE")
viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID") viper.BindEnv("generic-client-id", "GENERIC_CLIENT_ID")
viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET") viper.BindEnv("generic-client-secret", "GENERIC_CLIENT_SECRET")
viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE") viper.BindEnv("generic-client-secret-file", "GENERIC_CLIENT_SECRET_FILE")

View File

@@ -2,7 +2,6 @@ import { Grid, Button } from "@mantine/core";
import { GithubIcon } from "../../icons/github"; import { GithubIcon } from "../../icons/github";
import { GoogleIcon } from "../../icons/google"; import { GoogleIcon } from "../../icons/google";
import { OAuthIcon } from "../../icons/oauth"; import { OAuthIcon } from "../../icons/oauth";
import { TailscaleIcon } from "../../icons/tailscale";
interface OAuthButtonsProps { interface OAuthButtonsProps {
oauthProviders: string[]; oauthProviders: string[];
@@ -41,19 +40,6 @@ export const OAuthButtons = (props: OAuthButtonsProps) => {
</Button> </Button>
</Grid.Col> </Grid.Col>
)} )}
{oauthProviders.includes("tailscale") && (
<Grid.Col span="content">
<Button
radius="xl"
leftSection={<TailscaleIcon style={{ width: 14, height: 14 }} />}
variant="default"
onClick={() => mutate("tailscale")}
loading={isLoading}
>
Tailscale
</Button>
</Grid.Col>
)}
{oauthProviders.includes("generic") && ( {oauthProviders.includes("generic") && (
<Grid.Col span="content"> <Grid.Col span="content">
<Button <Button

View File

@@ -1,55 +0,0 @@
import type { SVGProps } from "react";
export function TailscaleIcon(props: SVGProps<SVGSVGElement>) {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
width={24}
height={24}
{...props}
>
<style>{".st0{opacity:0.2;fill:#CCCAC9;}.st1{fill:#FFFFFF;}"}</style>
<g>
<g>
<path
className="st0"
d="M65.6,127.7c35.3,0,63.9-28.6,63.9-63.9S100.9,0,65.6,0S1.8,28.6,1.8,63.9S30.4,127.7,65.6,127.7z"
/>
<path
className="st1"
d="M65.6,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9S1.8,219,1.8,254.2S30.4,318.1,65.6,318.1z"
/>
<path
className="st0"
d="M65.6,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9S1.8,412.9,1.8,448.1S30.4,512,65.6,512z"
/>
<path
className="st1"
d="M257.2,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S221.9,318.1,257.2,318.1z"
/>
<path
className="st1"
d="M257.2,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S221.9,512,257.2,512z"
/>
<path
className="st0"
d="M257.2,127.7c35.3,0,63.9-28.6,63.9-63.9S292.5,0,257.2,0s-63.9,28.6-63.9,63.9S221.9,127.7,257.2,127.7z"
/>
<path
className="st0"
d="M446.4,127.7c35.3,0,63.9-28.6,63.9-63.9S481.6,0,446.4,0c-35.3,0-63.9,28.6-63.9,63.9S411.1,127.7,446.4,127.7z"
/>
<path
className="st1"
d="M446.4,318.1c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S411.1,318.1,446.4,318.1z"
/>
<path
className="st0"
d="M446.4,512c35.3,0,63.9-28.6,63.9-63.9s-28.6-63.9-63.9-63.9s-63.9,28.6-63.9,63.9S411.1,512,446.4,512z"
/>
</g>
</g>
</svg>
);
}

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -1,46 +1,46 @@
{ {
"loginTitle": "Welcome back, login with", "loginTitle": "مرحبا بعودتك، قم بتسجيل الدخول باستخدام",
"loginDivider": "Or continue with password", "loginDivider": "أو المتابعة بكلمة المرور",
"loginUsername": "Username", "loginUsername": "اسم المستخدم",
"loginPassword": "Password", "loginPassword": "كلمة المرور",
"loginSubmit": "Login", "loginSubmit": "تسجيل الدخول",
"loginFailTitle": "Failed to log in", "loginFailTitle": "فشل تسجيل الدخول",
"loginFailSubtitle": "Please check your username and password", "loginFailSubtitle": "الرجاء التحقق من اسم المستخدم وكلمة المرور",
"loginFailRateLimit": "You failed to login too many times, please try again later", "loginFailRateLimit": "فشلت في تسجيل الدخول عدة مرات، الرجاء المحاولة مرة أخرى لاحقا",
"loginSuccessTitle": "Logged in", "loginSuccessTitle": "تم تسجيل الدخول",
"loginSuccessSubtitle": "Welcome back!", "loginSuccessSubtitle": "مرحبا بعودتك!",
"loginOauthFailTitle": "Internal error", "loginOauthFailTitle": "خطأ داخلي",
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "فشل في الحصول على رابط OAuth",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "إعادة توجيه",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "إعادة توجيه إلى مزود OAuth الخاص بك",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "إعادة توجيه...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "يجب إعادة توجيهك إلى التطبيق قريبا",
"continueInvalidRedirectTitle": "Invalid redirect", "continueInvalidRedirectTitle": "إعادة توجيه غير صالحة",
"continueInvalidRedirectSubtitle": "The redirect URL is invalid", "continueInvalidRedirectSubtitle": "رابط إعادة التوجيه غير صالح",
"continueInsecureRedirectTitle": "Insecure redirect", "continueInsecureRedirectTitle": "إعادة توجيه غير آمنة",
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?", "continueInsecureRedirectSubtitle": "أنت تحاول إعادة التوجيه من <Code>https</Code> إلى <Code>http</Code>، هل أنت متأكد أنك تريد المتابعة؟",
"continueTitle": "Continue", "continueTitle": "متابعة",
"continueSubtitle": "Click the button to continue to your app.", "continueSubtitle": "انقر الزر للمتابعة إلى التطبيق الخاص بك.",
"internalErrorTitle": "Internal Server Error", "internalErrorTitle": "خطأ داخلي في الخادم",
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.", "internalErrorSubtitle": "حدث خطأ على الخادم ولا يمكن حاليا تلبية طلبك.",
"internalErrorButton": "Try again", "internalErrorButton": "حاول مجددا",
"logoutFailTitle": "Failed to log out", "logoutFailTitle": "فشل تسجيل الخروج",
"logoutFailSubtitle": "Please try again", "logoutFailSubtitle": "يرجى إعادة المحاولة",
"logoutSuccessTitle": "Logged out", "logoutSuccessTitle": "تم تسجيل الخروج",
"logoutSuccessSubtitle": "You have been logged out", "logoutSuccessSubtitle": "تم تسجيل خروجك",
"logoutTitle": "Logout", "logoutTitle": "تسجيل الخروج",
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.", "logoutUsernameSubtitle": "أنت حاليا مسجل الدخول ك <Code>{{username}}</Code>، انقر الزر أدناه لتسجيل الخروج.",
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.", "logoutOauthSubtitle": "أنت حاليا مسجل الدخول ك <Code>{{username}}</Code> باستخدام مزود OAuth {{provider}} ، انقر الزر أدناه لتسجيل الخروج.",
"notFoundTitle": "Page not found", "notFoundTitle": "الصفحة غير موجودة",
"notFoundSubtitle": "The page you are looking for does not exist.", "notFoundSubtitle": "الصفحة التي تبحث عنها غير موجودة.",
"notFoundButton": "Go home", "notFoundButton": "انتقل إلى الرئيسية",
"totpFailTitle": "Failed to verify code", "totpFailTitle": "فشل في التحقق من الرمز",
"totpFailSubtitle": "Please check your code and try again", "totpFailSubtitle": "الرجاء التحقق من الرمز الخاص بك وحاول مرة أخرى",
"totpSuccessTitle": "Verified", "totpSuccessTitle": "تم التحقق",
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "إعادة توجيه إلى تطبيقك",
"totpTitle": "Enter your TOTP code", "totpTitle": "أدخل رمز TOTP الخاص بك",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "غير مرخص",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "المستخدم الذي يحمل اسم المستخدم <Code>{{username}}</Code> غير مصرح له بالوصول إلى المورد <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "المستخدم الذي يحمل اسم المستخدم <Code>{{username}}</Code> غير مصرح له بتسجيل الدخول.",
"unauthorizedButton": "Try again" "unauthorizedButton": "حاول مجددا"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -1,27 +1,27 @@
{ {
"loginTitle": "Welcome back, login with", "loginTitle": "Willkommen zurück, logge dich ein mit",
"loginDivider": "Or continue with password", "loginDivider": "Oder mit Passwort fortfahren",
"loginUsername": "Username", "loginUsername": "Benutzername",
"loginPassword": "Password", "loginPassword": "Passwort",
"loginSubmit": "Login", "loginSubmit": "Anmelden",
"loginFailTitle": "Failed to log in", "loginFailTitle": "Login fehlgeschlagen",
"loginFailSubtitle": "Please check your username and password", "loginFailSubtitle": "Bitte überprüfe deinen Benutzernamen und Passwort",
"loginFailRateLimit": "You failed to login too many times, please try again later", "loginFailRateLimit": "Sie konnten sich zu oft nicht einloggen, bitte versuchen Sie es später erneut",
"loginSuccessTitle": "Logged in", "loginSuccessTitle": "Logged in",
"loginSuccessSubtitle": "Welcome back!", "loginSuccessSubtitle": "Willkommen zurück!",
"loginOauthFailTitle": "Internal error", "loginOauthFailTitle": "Interner Fehler",
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "Fehler beim Abrufen der OAuth-URL",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "Redirecting",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "Redirecting to your OAuth provider",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "Redirecting...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "You should be redirected to the app soon",
"continueInvalidRedirectTitle": "Invalid redirect", "continueInvalidRedirectTitle": "Ungültige Weiterleitung",
"continueInvalidRedirectSubtitle": "The redirect URL is invalid", "continueInvalidRedirectSubtitle": "Die Weiterleitungs-URL ist ungültig",
"continueInsecureRedirectTitle": "Insecure redirect", "continueInsecureRedirectTitle": "Unsichere Weiterleitung",
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?", "continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?",
"continueTitle": "Continue", "continueTitle": "Weiter",
"continueSubtitle": "Click the button to continue to your app.", "continueSubtitle": "Click the button to continue to your app.",
"internalErrorTitle": "Internal Server Error", "internalErrorTitle": "Interner Serverfehler",
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.", "internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.",
"internalErrorButton": "Try again", "internalErrorButton": "Try again",
"logoutFailTitle": "Failed to log out", "logoutFailTitle": "Failed to log out",
@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας", "totpSuccessSubtitle": "Ανακατεύθυνση στην εφαρμογή σας",
"totpTitle": "Εισάγετε τον κωδικό TOTP", "totpTitle": "Εισάγετε τον κωδικό TOTP",
"unauthorizedTitle": "Μη εξουσιοδοτημένο", "unauthorizedTitle": "Μη εξουσιοδοτημένο",
"unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}<Code/> δεν έχει άδεια πρόσβασης στον πόρο <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν έχει άδεια πρόσβασης στον πόρο <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}<Code/> δεν είναι εξουσιοδοτημένος να συνδεθεί.", "unaothorizedLoginSubtitle": "Ο χρήστης με όνομα χρήστη <Code>{{username}}</Code> δεν είναι εξουσιοδοτημένος να συνδεθεί.",
"unauthorizedButton": "Προσπαθήστε ξανά" "unauthorizedButton": "Προσπαθήστε ξανά"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirection vers votre application", "totpSuccessSubtitle": "Redirection vers votre application",
"totpTitle": "Saisissez votre code TOTP", "totpTitle": "Saisissez votre code TOTP",
"unauthorizedTitle": "Non autorisé", "unauthorizedTitle": "Non autorisé",
"unauthorizedResourceSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}<Code/> n'est pas autorisé à accéder à la ressource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}</Code> n'est pas autorisé à accéder à la ressource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}<Code/> n'est pas autorisé à se connecter.", "unaothorizedLoginSubtitle": "L'utilisateur avec le nom d'utilisateur <Code>{{username}}</Code> n'est pas autorisé à se connecter.",
"unauthorizedButton": "Réessayer" "unauthorizedButton": "Réessayer"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -1,46 +1,46 @@
{ {
"loginTitle": "Welcome back, login with", "loginTitle": "Welkom terug, log in met",
"loginDivider": "Or continue with password", "loginDivider": "Of ga door met wachtwoord",
"loginUsername": "Username", "loginUsername": "Gebruikersnaam",
"loginPassword": "Password", "loginPassword": "Wachtwoord",
"loginSubmit": "Login", "loginSubmit": "Log in",
"loginFailTitle": "Failed to log in", "loginFailTitle": "Mislukt om in te loggen",
"loginFailSubtitle": "Please check your username and password", "loginFailSubtitle": "Controleer je gebruikersnaam en wachtwoord",
"loginFailRateLimit": "You failed to login too many times, please try again later", "loginFailRateLimit": "Inloggen te vaak mislukt, probeer het later opnieuw",
"loginSuccessTitle": "Logged in", "loginSuccessTitle": "Ingelogd",
"loginSuccessSubtitle": "Welcome back!", "loginSuccessSubtitle": "Welkom terug!",
"loginOauthFailTitle": "Internal error", "loginOauthFailTitle": "Interne fout",
"loginOauthFailSubtitle": "Failed to get OAuth URL", "loginOauthFailSubtitle": "Fout bij het ophalen van OAuth URL",
"loginOauthSuccessTitle": "Redirecting", "loginOauthSuccessTitle": "Omleiden",
"loginOauthSuccessSubtitle": "Redirecting to your OAuth provider", "loginOauthSuccessSubtitle": "Omleiden naar je OAuth provider",
"continueRedirectingTitle": "Redirecting...", "continueRedirectingTitle": "Omleiden...",
"continueRedirectingSubtitle": "You should be redirected to the app soon", "continueRedirectingSubtitle": "Je wordt naar de app doorgestuurd",
"continueInvalidRedirectTitle": "Invalid redirect", "continueInvalidRedirectTitle": "Ongeldige omleiding",
"continueInvalidRedirectSubtitle": "The redirect URL is invalid", "continueInvalidRedirectSubtitle": "De omleidings-URL is ongeldig",
"continueInsecureRedirectTitle": "Insecure redirect", "continueInsecureRedirectTitle": "Onveilige doorverwijzing",
"continueInsecureRedirectSubtitle": "You are trying to redirect from <Code>https</Code> to <Code>http</Code>, are you sure you want to continue?", "continueInsecureRedirectSubtitle": "Je probeert door te verwijzen van <Code>https</Code> naar <Code>http</Code>, weet je zeker dat je wilt doorgaan?",
"continueTitle": "Continue", "continueTitle": "Ga verder",
"continueSubtitle": "Click the button to continue to your app.", "continueSubtitle": "Klik op de knop om door te gaan naar de app.",
"internalErrorTitle": "Internal Server Error", "internalErrorTitle": "Interne server fout",
"internalErrorSubtitle": "An error occurred on the server and it currently cannot serve your request.", "internalErrorSubtitle": "Er is een fout opgetreden op de server en het kan momenteel niet voldoen aan je verzoek.",
"internalErrorButton": "Try again", "internalErrorButton": "Opnieuw proberen",
"logoutFailTitle": "Failed to log out", "logoutFailTitle": "Afmelden mislukt",
"logoutFailSubtitle": "Please try again", "logoutFailSubtitle": "Probeer het opnieuw",
"logoutSuccessTitle": "Logged out", "logoutSuccessTitle": "Afgemeld",
"logoutSuccessSubtitle": "You have been logged out", "logoutSuccessSubtitle": "Je bent afgemeld",
"logoutTitle": "Logout", "logoutTitle": "Afmelden",
"logoutUsernameSubtitle": "You are currently logged in as <Code>{{username}}</Code>, click the button below to logout.", "logoutUsernameSubtitle": "Je bent momenteel ingelogd als <Code>{{username}}</Code>, klik op de knop hieronder om uit te loggen.",
"logoutOauthSubtitle": "You are currently logged in as <Code>{{username}}</Code> using the {{provider}} OAuth provider, click the button below to logout.", "logoutOauthSubtitle": "Je bent momenteel ingelogd als <Code>{{username}}</Code> met behulp van de {{provider}} OAuth provider, klik op de knop hieronder om uit te loggen.",
"notFoundTitle": "Page not found", "notFoundTitle": "Pagina niet gevonden",
"notFoundSubtitle": "The page you are looking for does not exist.", "notFoundSubtitle": "De pagina die je zoekt bestaat niet.",
"notFoundButton": "Go home", "notFoundButton": "Naar startpagina",
"totpFailTitle": "Failed to verify code", "totpFailTitle": "Verifiëren van code mislukt",
"totpFailSubtitle": "Please check your code and try again", "totpFailSubtitle": "Controleer je code en probeer het opnieuw",
"totpSuccessTitle": "Verified", "totpSuccessTitle": "Geverifiëerd",
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Omleiden naar je app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Voer je TOTP-code in",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Ongeautoriseerd",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "De gebruiker met gebruikersnaam <Code>{{username}}</Code> heeft geen toegang tot de bron <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "De gebruiker met gebruikersnaam <Code>{{username}}</Code> is niet gemachtigd om in te loggen.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Opnieuw proberen"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Przekierowywanie do aplikacji", "totpSuccessSubtitle": "Przekierowywanie do aplikacji",
"totpTitle": "Wprowadź kod TOTP", "totpTitle": "Wprowadź kod TOTP",
"unauthorizedTitle": "Nieautoryzowany", "unauthorizedTitle": "Nieautoryzowany",
"unauthorizedResourceSubtitle": "Użytkownik o nazwie <Code>{{username}}<Code/> nie jest upoważniony do uzyskania dostępu do zasobu <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "Użytkownik o nazwie <Code>{{username}}</Code> nie jest upoważniony do uzyskania dostępu do zasobu <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "Użytkownik o nazwie <Code>{{username}}<Code/> nie jest upoważniony do logowania.", "unaothorizedLoginSubtitle": "Użytkownik o nazwie <Code>{{username}}</Code> nie jest upoważniony do logowania.",
"unauthorizedButton": "Spróbuj ponownie" "unauthorizedButton": "Spróbuj ponownie"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -40,7 +40,7 @@
"totpSuccessSubtitle": "Redirecting to your app", "totpSuccessSubtitle": "Redirecting to your app",
"totpTitle": "Enter your TOTP code", "totpTitle": "Enter your TOTP code",
"unauthorizedTitle": "Unauthorized", "unauthorizedTitle": "Unauthorized",
"unauthorizedResourceSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to access the resource <Code>{{resource}}</Code>.", "unauthorizedResourceSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to access the resource <Code>{{resource}}</Code>.",
"unaothorizedLoginSubtitle": "The user with username <Code>{{username}}<Code/> is not authorized to login.", "unaothorizedLoginSubtitle": "The user with username <Code>{{username}}</Code> is not authorized to login.",
"unauthorizedButton": "Try again" "unauthorizedButton": "Try again"
} }

View File

@@ -29,7 +29,7 @@ const queryClient = new QueryClient({
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<StrictMode> <StrictMode>
<MantineProvider forceColorScheme="dark"> <MantineProvider defaultColorScheme="auto">
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<Notifications /> <Notifications />
<AppContextProvider> <AppContextProvider>

View File

@@ -1 +1 @@
v3.2.0 v3.2.1

View File

@@ -295,12 +295,11 @@ func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bo
// Check if user is allowed // Check if user is allowed
if len(labels.Users) != 0 { if len(labels.Users) != 0 {
log.Debug().Msg("Checking users") log.Debug().Msg("Checking users")
if len(labels.Users) == 0 {
return true, nil
}
if slices.Contains(labels.Users, context.Username) { if slices.Contains(labels.Users, context.Username) {
return true, nil return true, nil
} }
} else {
return true, nil
} }
// Not allowed // Not allowed

View File

@@ -2,7 +2,6 @@ package handlers
import ( import (
"fmt" "fmt"
"math/rand/v2"
"net/http" "net/http"
"strings" "strings"
"tinyauth/internal/auth" "tinyauth/internal/auth"
@@ -531,32 +530,6 @@ func (h *Handlers) OauthUrlHandler(c *gin.Context) {
}) })
} }
// Tailscale does not have an auth url so we create a random code (does not need to be secure) to avoid caching and send it
if request.Provider == "tailscale" {
// Build tailscale query
queries, err := query.Values(types.TailscaleQuery{
Code: (1000 + rand.IntN(9000)),
})
// Handle error
if err != nil {
log.Error().Err(err).Msg("Failed to build queries")
c.JSON(500, gin.H{
"status": 500,
"message": "Internal Server Error",
})
return
}
// Return tailscale URL (immidiately redirects to the callback)
c.JSON(200, gin.H{
"status": 200,
"message": "OK",
"url": fmt.Sprintf("%s/api/oauth/callback/tailscale?%s", h.Config.AppURL, queries.Encode()),
})
return
}
// Return auth URL // Return auth URL
c.JSON(200, gin.H{ c.JSON(200, gin.H{
"status": 200, "status": 200,

View File

@@ -17,11 +17,10 @@ func NewProviders(config types.OAuthConfig) *Providers {
} }
type Providers struct { type Providers struct {
Config types.OAuthConfig Config types.OAuthConfig
Github *oauth.OAuth Github *oauth.OAuth
Google *oauth.OAuth Google *oauth.OAuth
Tailscale *oauth.OAuth Generic *oauth.OAuth
Generic *oauth.OAuth
} }
func (providers *Providers) Init() { func (providers *Providers) Init() {
@@ -59,22 +58,6 @@ func (providers *Providers) Init() {
providers.Google.Init() providers.Google.Init()
} }
if providers.Config.TailscaleClientId != "" && providers.Config.TailscaleClientSecret != "" {
log.Info().Msg("Initializing Tailscale OAuth")
// Create a new oauth provider with the tailscale config
providers.Tailscale = oauth.NewOAuth(oauth2.Config{
ClientID: providers.Config.TailscaleClientId,
ClientSecret: providers.Config.TailscaleClientSecret,
RedirectURL: fmt.Sprintf("%s/api/oauth/callback/tailscale", providers.Config.AppURL),
Scopes: TailscaleScopes(),
Endpoint: TailscaleEndpoint,
})
// Initialize the oauth provider
providers.Tailscale.Init()
}
// If we have a client id and secret for generic oauth, initialize the oauth provider // If we have a client id and secret for generic oauth, initialize the oauth provider
if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" { if providers.Config.GenericClientId != "" && providers.Config.GenericClientSecret != "" {
log.Info().Msg("Initializing Generic OAuth") log.Info().Msg("Initializing Generic OAuth")
@@ -103,8 +86,6 @@ func (providers *Providers) GetProvider(provider string) *oauth.OAuth {
return providers.Github return providers.Github
case "google": case "google":
return providers.Google return providers.Google
case "tailscale":
return providers.Tailscale
case "generic": case "generic":
return providers.Generic return providers.Generic
default: default:
@@ -161,30 +142,6 @@ func (providers *Providers) GetUser(provider string) (string, error) {
log.Debug().Msg("Got email from google") log.Debug().Msg("Got email from google")
// Return the email
return email, nil
case "tailscale":
// If the tailscale provider is not configured, return an error
if providers.Tailscale == nil {
log.Debug().Msg("Tailscale provider not configured")
return "", nil
}
// Get the client from the tailscale provider
client := providers.Tailscale.GetClient()
log.Debug().Msg("Got client from tailscale")
// Get the email from the tailscale provider
email, err := GetTailscaleEmail(client)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Got email from tailscale")
// Return the email // Return the email
return email, nil return email, nil
case "generic": case "generic":
@@ -225,9 +182,6 @@ func (provider *Providers) GetConfiguredProviders() []string {
if provider.Google != nil { if provider.Google != nil {
providers = append(providers, "google") providers = append(providers, "google")
} }
if provider.Tailscale != nil {
providers = append(providers, "tailscale")
}
if provider.Generic != nil { if provider.Generic != nil {
providers = append(providers, "generic") providers = append(providers, "generic")
} }

View File

@@ -1,68 +0,0 @@
package providers
import (
"encoding/json"
"io"
"net/http"
"github.com/rs/zerolog/log"
"golang.org/x/oauth2"
)
// The tailscale email is the loginName
type TailscaleUser struct {
LoginName string `json:"loginName"`
}
// The response from the tailscale user info endpoint
type TailscaleUserInfoResponse struct {
Users []TailscaleUser `json:"users"`
}
// The scopes required for the tailscale provider
func TailscaleScopes() []string {
return []string{"users:read"}
}
// The tailscale endpoint
var TailscaleEndpoint = oauth2.Endpoint{
TokenURL: "https://api.tailscale.com/api/v2/oauth/token",
}
func GetTailscaleEmail(client *http.Client) (string, error) {
// Get the user info from tailscale using the oauth http client
res, err := client.Get("https://api.tailscale.com/api/v2/tailnet/-/users")
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Got response from tailscale")
// Read the body of the response
body, err := io.ReadAll(res.Body)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Read body from tailscale")
// Parse the body into a user struct
var users TailscaleUserInfoResponse
// Unmarshal the body into the user struct
err = json.Unmarshal(body, &users)
// Check if there was an error
if err != nil {
return "", err
}
log.Debug().Msg("Parsed users from tailscale")
// Return the email of the first user
return users.Users[0].LoginName, nil
}

View File

@@ -22,11 +22,6 @@ type UnauthorizedQuery struct {
Resource string `url:"resource"` Resource string `url:"resource"`
} }
// TailscaleQuery is the query parameters for the tailscale endpoint
type TailscaleQuery struct {
Code int `url:"code"`
}
// Proxy is the uri parameters for the proxy endpoint // Proxy is the uri parameters for the proxy endpoint
type Proxy struct { type Proxy struct {
Proxy string `uri:"proxy" binding:"required"` Proxy string `uri:"proxy" binding:"required"`

View File

@@ -2,39 +2,36 @@ package types
// Config is the configuration for the tinyauth server // Config is the configuration for the tinyauth server
type Config struct { type Config struct {
Port int `mapstructure:"port" validate:"required"` Port int `mapstructure:"port" validate:"required"`
Address string `validate:"required,ip4_addr" mapstructure:"address"` Address string `validate:"required,ip4_addr" mapstructure:"address"`
Secret string `validate:"required,len=32" mapstructure:"secret"` Secret string `validate:"required,len=32" mapstructure:"secret"`
SecretFile string `mapstructure:"secret-file"` SecretFile string `mapstructure:"secret-file"`
AppURL string `validate:"required,url" mapstructure:"app-url"` AppURL string `validate:"required,url" mapstructure:"app-url"`
Users string `mapstructure:"users"` Users string `mapstructure:"users"`
UsersFile string `mapstructure:"users-file"` UsersFile string `mapstructure:"users-file"`
CookieSecure bool `mapstructure:"cookie-secure"` CookieSecure bool `mapstructure:"cookie-secure"`
GithubClientId string `mapstructure:"github-client-id"` GithubClientId string `mapstructure:"github-client-id"`
GithubClientSecret string `mapstructure:"github-client-secret"` GithubClientSecret string `mapstructure:"github-client-secret"`
GithubClientSecretFile string `mapstructure:"github-client-secret-file"` GithubClientSecretFile string `mapstructure:"github-client-secret-file"`
GoogleClientId string `mapstructure:"google-client-id"` GoogleClientId string `mapstructure:"google-client-id"`
GoogleClientSecret string `mapstructure:"google-client-secret"` GoogleClientSecret string `mapstructure:"google-client-secret"`
GoogleClientSecretFile string `mapstructure:"google-client-secret-file"` GoogleClientSecretFile string `mapstructure:"google-client-secret-file"`
TailscaleClientId string `mapstructure:"tailscale-client-id"` GenericClientId string `mapstructure:"generic-client-id"`
TailscaleClientSecret string `mapstructure:"tailscale-client-secret"` GenericClientSecret string `mapstructure:"generic-client-secret"`
TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"` GenericClientSecretFile string `mapstructure:"generic-client-secret-file"`
GenericClientId string `mapstructure:"generic-client-id"` GenericScopes string `mapstructure:"generic-scopes"`
GenericClientSecret string `mapstructure:"generic-client-secret"` GenericAuthURL string `mapstructure:"generic-auth-url"`
GenericClientSecretFile string `mapstructure:"generic-client-secret-file"` GenericTokenURL string `mapstructure:"generic-token-url"`
GenericScopes string `mapstructure:"generic-scopes"` GenericUserURL string `mapstructure:"generic-user-url"`
GenericAuthURL string `mapstructure:"generic-auth-url"` GenericName string `mapstructure:"generic-name"`
GenericTokenURL string `mapstructure:"generic-token-url"` DisableContinue bool `mapstructure:"disable-continue"`
GenericUserURL string `mapstructure:"generic-user-url"` OAuthWhitelist string `mapstructure:"oauth-whitelist"`
GenericName string `mapstructure:"generic-name"` SessionExpiry int `mapstructure:"session-expiry"`
DisableContinue bool `mapstructure:"disable-continue"` LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"`
OAuthWhitelist string `mapstructure:"oauth-whitelist"` Title string `mapstructure:"app-title"`
SessionExpiry int `mapstructure:"session-expiry"` EnvFile string `mapstructure:"env-file"`
LogLevel int8 `mapstructure:"log-level" validate:"min=-1,max=5"` LoginTimeout int `mapstructure:"login-timeout"`
Title string `mapstructure:"app-title"` LoginMaxRetries int `mapstructure:"login-max-retries"`
EnvFile string `mapstructure:"env-file"`
LoginTimeout int `mapstructure:"login-timeout"`
LoginMaxRetries int `mapstructure:"login-max-retries"`
} }
// Server configuration // Server configuration
@@ -47,19 +44,17 @@ type HandlersConfig struct {
// OAuthConfig is the configuration for the providers // OAuthConfig is the configuration for the providers
type OAuthConfig struct { type OAuthConfig struct {
GithubClientId string GithubClientId string
GithubClientSecret string GithubClientSecret string
GoogleClientId string GoogleClientId string
GoogleClientSecret string GoogleClientSecret string
TailscaleClientId string GenericClientId string
TailscaleClientSecret string GenericClientSecret string
GenericClientId string GenericScopes []string
GenericClientSecret string GenericAuthURL string
GenericScopes []string GenericTokenURL string
GenericAuthURL string GenericUserURL string
GenericTokenURL string AppURL string
GenericUserURL string
AppURL string
} }
// APIConfig is the configuration for the API // APIConfig is the configuration for the API

View File

@@ -130,7 +130,7 @@ func GetSecret(conf string, file string) string {
} }
// Return the contents of the file // Return the contents of the file
return contents return ParseSecretFile(contents)
} }
// Get the users from the config or file // Get the users from the config or file
@@ -213,7 +213,7 @@ func GetTinyauthLabels(labels map[string]string) types.TinyauthLabels {
// Check if any of the OAuth providers are configured based on the client id and secret // Check if any of the OAuth providers are configured based on the client id and secret
func OAuthConfigured(config types.Config) bool { func OAuthConfigured(config types.Config) bool {
return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "") || (config.TailscaleClientId != "" && config.TailscaleClientSecret != "") return (config.GithubClientId != "" && config.GithubClientSecret != "") || (config.GoogleClientId != "" && config.GoogleClientSecret != "") || (config.GenericClientId != "" && config.GenericClientSecret != "")
} }
// Filter helper function // Filter helper function
@@ -241,23 +241,21 @@ func ParseUser(user string) (types.User, error) {
return types.User{}, errors.New("invalid user format") return types.User{}, errors.New("invalid user format")
} }
// Check if the user has a totp secret // Check for empty strings
if len(userSplit) == 2 { for _, userPart := range userSplit {
// Check for empty username or password if strings.TrimSpace(userPart) == "" {
if userSplit[1] == "" || userSplit[0] == "" {
return types.User{}, errors.New("invalid user format") return types.User{}, errors.New("invalid user format")
} }
}
// Check if the user has a totp secret
if len(userSplit) == 2 {
return types.User{ return types.User{
Username: userSplit[0], Username: userSplit[0],
Password: userSplit[1], Password: userSplit[1],
}, nil }, nil
} }
// Check for empty username, password or totp secret
if userSplit[2] == "" || userSplit[1] == "" || userSplit[0] == "" {
return types.User{}, errors.New("invalid user format")
}
// Return the user struct // Return the user struct
return types.User{ return types.User{
Username: userSplit[0], Username: userSplit[0],
@@ -265,3 +263,23 @@ func ParseUser(user string) (types.User, error) {
TotpSecret: userSplit[2], TotpSecret: userSplit[2],
}, nil }, nil
} }
// Parse secret file
func ParseSecretFile(contents string) string {
// Split to lines
lines := strings.Split(contents, "\n")
// Loop through the lines
for _, line := range lines {
// Check if the line is empty
if strings.TrimSpace(line) == "" {
continue
}
// Return the line
return strings.TrimSpace(line)
}
// Return an empty string
return ""
}

View File

@@ -1,6 +1,7 @@
package utils_test package utils_test
import ( import (
"fmt"
"os" "os"
"reflect" "reflect"
"testing" "testing"
@@ -123,7 +124,7 @@ func TestGetSecret(t *testing.T) {
expected := "test" expected := "test"
// Create file // Create file
err := os.WriteFile(file, []byte(expected), 0644) err := os.WriteFile(file, []byte(fmt.Sprintf("\n\n \n\n\n %s \n\n \n ", expected)), 0644)
// Check if there was an error // Check if there was an error
if err != nil { if err != nil {