mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 14:15:50 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			v3.2.0-bet
			...
			v3.2.1-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 525f4f3041 | ||
|   | 8a21345706 | ||
|   | 1169c633cc | ||
|   | 2242c9c1e6 | ||
|   | 939919df39 | ||
|   | a579cf37ce | ||
|   | 2647aa07b4 | ||
|   | f68c580e11 | ||
|   | 9b39a2b856 | ||
|   | 6d17ce699a | ||
|   | 20dbb35d44 | ||
|   | 36d9dd7354 | ||
|   | 5129f9bff8 | 
| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										105
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										105
									
								
								.github/workflows/release.yml
									
									
									
									
										vendored
									
									
								
							| @@ -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/* | ||||||
|   | |||||||
| @@ -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. | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| > [!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. | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								assets/login.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/login.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 93 KiB | 
							
								
								
									
										
											BIN
										
									
								
								assets/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 98 KiB | 
							
								
								
									
										31
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -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") | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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> |  | ||||||
|   ); |  | ||||||
| } |  | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -1,12 +1,12 @@ | |||||||
| { | { | ||||||
|     "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": "Welcome back!", | ||||||
|     "loginOauthFailTitle": "Internal error", |     "loginOauthFailTitle": "Internal error", | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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": "Προσπαθήστε ξανά" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -1,11 +1,11 @@ | |||||||
| { | { | ||||||
|     "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": "Gelieve uw gebruikersnaam en wachtwoord te controleren", | ||||||
|     "loginFailRateLimit": "You failed to login too many times, please try again later", |     "loginFailRateLimit": "You failed to login too many times, please try again later", | ||||||
|     "loginSuccessTitle": "Logged in", |     "loginSuccessTitle": "Logged in", | ||||||
|     "loginSuccessSubtitle": "Welcome back!", |     "loginSuccessSubtitle": "Welcome back!", | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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" | ||||||
| } | } | ||||||
| @@ -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> | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| v3.2.0 | v3.2.1 | ||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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, | ||||||
|   | |||||||
| @@ -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") | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -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 |  | ||||||
| } |  | ||||||
| @@ -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"` | ||||||
|   | |||||||
| @@ -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 | ||||||
|   | |||||||
| @@ -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 "" | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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 { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user