mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-31 06:05:43 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			v2.0.0-alp
			...
			v2.0.2-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6f184856f1 | ||
|   | e2e3b3bdc6 | ||
|   | 3efcb26db1 | ||
|   | c54267f50d | ||
|   | 4de12ce5c1 | ||
|   | 0cf0aafc14 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -11,4 +11,5 @@ docker-compose.test.yml | |||||||
| users.txt | users.txt | ||||||
|  |  | ||||||
| # secret test file | # secret test file | ||||||
| secret.txt | secret.txt | ||||||
|  | secret_oauth.txt | ||||||
							
								
								
									
										35
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -1,9 +1,12 @@ | |||||||
| package cmd | package cmd | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"os" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"time" | ||||||
| 	cmd "tinyauth/cmd/user" | 	cmd "tinyauth/cmd/user" | ||||||
| 	"tinyauth/internal/api" | 	"tinyauth/internal/api" | ||||||
|  | 	"tinyauth/internal/assets" | ||||||
| 	"tinyauth/internal/auth" | 	"tinyauth/internal/auth" | ||||||
| 	"tinyauth/internal/hooks" | 	"tinyauth/internal/hooks" | ||||||
| 	"tinyauth/internal/providers" | 	"tinyauth/internal/providers" | ||||||
| @@ -22,21 +25,28 @@ var rootCmd = &cobra.Command{ | |||||||
| 	Short: "The simplest way to protect your apps with a login screen.", | 	Short: "The simplest way to protect your apps with a login screen.", | ||||||
| 	Long:  `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.`, | 	Long:  `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.`, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 		// Logger | ||||||
|  | 		log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger().Level(zerolog.FatalLevel) | ||||||
|  |  | ||||||
| 		// Get config | 		// Get config | ||||||
| 		log.Info().Msg("Parsing config") |  | ||||||
| 		var config types.Config | 		var config types.Config | ||||||
| 		parseErr := viper.Unmarshal(&config) | 		parseErr := viper.Unmarshal(&config) | ||||||
| 		HandleError(parseErr, "Failed to parse config") | 		HandleError(parseErr, "Failed to parse config") | ||||||
|  |  | ||||||
|  | 		// Secrets | ||||||
|  | 		config.Secret = utils.GetSecret(config.Secret, config.SecretFile) | ||||||
|  | 		config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) | ||||||
|  | 		config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) | ||||||
|  | 		config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) | ||||||
|  |  | ||||||
| 		// Validate config | 		// Validate config | ||||||
| 		log.Info().Msg("Validating config") |  | ||||||
| 		validator := validator.New() | 		validator := validator.New() | ||||||
| 		validateErr := validator.Struct(config) | 		validateErr := validator.Struct(config) | ||||||
| 		HandleError(validateErr, "Invalid config") | 		HandleError(validateErr, "Failed to validate config") | ||||||
|  |  | ||||||
| 		// Set log level | 		// Logger | ||||||
| 		log.Info().Int8("log_level", config.LogLevel).Msg("Setting log level") | 		log.Logger = log.Level(zerolog.Level(config.LogLevel)) | ||||||
| 		log.Logger = log.Logger.Level(zerolog.Level(config.LogLevel)) | 		log.Info().Str("version", assets.Version).Msg("Starting tinyauth") | ||||||
|  |  | ||||||
| 		// Users | 		// Users | ||||||
| 		log.Info().Msg("Parsing users") | 		log.Info().Msg("Parsing users") | ||||||
| @@ -46,17 +56,9 @@ var rootCmd = &cobra.Command{ | |||||||
| 			log.Fatal().Err(usersErr).Msg("Failed to parse users") | 			log.Fatal().Err(usersErr).Msg("Failed to parse users") | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Secrets |  | ||||||
| 		log.Info().Msg("Parsing secrets") |  | ||||||
|  |  | ||||||
| 		config.Secret = utils.GetSecret(config.Secret, config.SecretFile) |  | ||||||
| 		config.GithubClientSecret = utils.GetSecret(config.GithubClientSecret, config.GithubClientSecretFile) |  | ||||||
| 		config.GoogleClientSecret = utils.GetSecret(config.GoogleClientSecret, config.GoogleClientSecretFile) |  | ||||||
| 		config.GenericClientSecret = utils.GetSecret(config.GenericClientSecret, config.GenericClientSecretFile) |  | ||||||
|  |  | ||||||
| 		// Create oauth whitelist | 		// Create oauth whitelist | ||||||
| 		oauthWhitelist := strings.Split(config.OAuthWhitelist, ",") | 		oauthWhitelist := strings.Split(config.OAuthWhitelist, ",") | ||||||
| 		log.Debug().Strs("oauth_whitelist", oauthWhitelist).Msg("Parsed OAuth whitelist") | 		log.Debug().Msg("Parsed OAuth whitelist") | ||||||
|  |  | ||||||
| 		// Create OAuth config | 		// Create OAuth config | ||||||
| 		oauthConfig := types.OAuthConfig{ | 		oauthConfig := types.OAuthConfig{ | ||||||
| @@ -72,7 +74,8 @@ var rootCmd = &cobra.Command{ | |||||||
| 			GenericUserURL:      config.GenericUserURL, | 			GenericUserURL:      config.GenericUserURL, | ||||||
| 			AppURL:              config.AppURL, | 			AppURL:              config.AppURL, | ||||||
| 		} | 		} | ||||||
| 		log.Debug().Interface("oauth_config", oauthConfig).Msg("Parsed OAuth config") |  | ||||||
|  | 		log.Debug().Msg("Parsed OAuth config") | ||||||
|  |  | ||||||
| 		// Create auth service | 		// Create auth service | ||||||
| 		auth := auth.NewAuth(users, oauthWhitelist) | 		auth := auth.NewAuth(users, oauthWhitelist) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/charmbracelet/huh" | 	"github.com/charmbracelet/huh" | ||||||
|  | 	"github.com/rs/zerolog" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"github.com/spf13/cobra" | 	"github.com/spf13/cobra" | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| @@ -21,6 +22,8 @@ var CreateCmd = &cobra.Command{ | |||||||
| 	Short: "Create a user", | 	Short: "Create a user", | ||||||
| 	Long:  `Create a user either interactively or by passing flags.`, | 	Long:  `Create a user either interactively or by passing flags.`, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 		log.Logger = log.Level(zerolog.InfoLevel) | ||||||
|  |  | ||||||
| 		if interactive { | 		if interactive { | ||||||
| 			form := huh.NewForm( | 			form := huh.NewForm( | ||||||
| 				huh.NewGroup( | 				huh.NewGroup( | ||||||
|   | |||||||
| @@ -8,12 +8,17 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| func UserCmd() *cobra.Command { | func UserCmd() *cobra.Command { | ||||||
|  | 	// Create the user command | ||||||
| 	userCmd := &cobra.Command{ | 	userCmd := &cobra.Command{ | ||||||
| 		Use:  "user", | 		Use:   "user", | ||||||
| 		Short: "User utilities", | 		Short: "User utilities", | ||||||
| 		Long: `Utilities for creating and verifying tinyauth compatible users.`, | 		Long:  `Utilities for creating and verifying tinyauth compatible users.`, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Add subcommands | ||||||
| 	userCmd.AddCommand(create.CreateCmd) | 	userCmd.AddCommand(create.CreateCmd) | ||||||
| 	userCmd.AddCommand(verify.VerifyCmd) | 	userCmd.AddCommand(verify.VerifyCmd) | ||||||
|  |  | ||||||
|  | 	// Return the user command | ||||||
| 	return userCmd | 	return userCmd | ||||||
| }	 | } | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/charmbracelet/huh" | 	"github.com/charmbracelet/huh" | ||||||
|  | 	"github.com/rs/zerolog" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"github.com/spf13/cobra" | 	"github.com/spf13/cobra" | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| @@ -21,6 +22,8 @@ var VerifyCmd = &cobra.Command{ | |||||||
| 	Short: "Verify a user is set up correctly", | 	Short: "Verify a user is set up correctly", | ||||||
| 	Long:  `Verify a user is set up correctly meaning that it has a correct username and password.`, | 	Long:  `Verify a user is set up correctly meaning that it has a correct username and password.`, | ||||||
| 	Run: func(cmd *cobra.Command, args []string) { | 	Run: func(cmd *cobra.Command, args []string) { | ||||||
|  | 		log.Logger = log.Level(zerolog.InfoLevel) | ||||||
|  |  | ||||||
| 		if interactive { | 		if interactive { | ||||||
| 			form := huh.NewForm( | 			form := huh.NewForm( | ||||||
| 				huh.NewGroup( | 				huh.NewGroup( | ||||||
|   | |||||||
| @@ -114,7 +114,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			RedirectURI: fmt.Sprintf("%s://%s%s", proto, host, uri), | 			RedirectURI: fmt.Sprintf("%s://%s%s", proto, host, uri), | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		log.Debug().Interface("queries", queries).Msg("Redirecting to login") | 		log.Debug().Interface("redirect_uri", fmt.Sprintf("%s://%s%s", proto, host, uri)).Msg("Redirecting to login") | ||||||
|  |  | ||||||
| 		if queryErr != nil { | 		if queryErr != nil { | ||||||
| 			log.Error().Err(queryErr).Msg("Failed to build query") | 			log.Error().Err(queryErr).Msg("Failed to build query") | ||||||
| @@ -142,7 +142,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debug().Interface("login", login).Msg("Got login request") | 		log.Debug().Msg("Got login request") | ||||||
|  |  | ||||||
| 		user := api.Auth.GetUser(login.Username) | 		user := api.Auth.GetUser(login.Username) | ||||||
|  |  | ||||||
| @@ -250,7 +250,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debug().Interface("request", request).Msg("Got OAuth request") | 		log.Debug().Msg("Got OAuth request") | ||||||
|  |  | ||||||
| 		provider := api.Providers.GetProvider(request.Provider) | 		provider := api.Providers.GetProvider(request.Provider) | ||||||
|  |  | ||||||
| @@ -266,7 +266,7 @@ func (api *API) SetupRoutes() { | |||||||
|  |  | ||||||
| 		authURL := provider.GetAuthURL() | 		authURL := provider.GetAuthURL() | ||||||
|  |  | ||||||
| 		log.Debug().Str("authURL", authURL).Msg("Got auth URL") | 		log.Debug().Msg("Got auth URL") | ||||||
|  |  | ||||||
| 		redirectURI := c.Query("redirect_uri") | 		redirectURI := c.Query("redirect_uri") | ||||||
|  |  | ||||||
| @@ -291,7 +291,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debug().Interface("providerName", providerName).Msg("Got provider name") | 		log.Debug().Interface("provider", providerName.Provider).Msg("Got provider name") | ||||||
|  |  | ||||||
| 		code := c.Query("code") | 		code := c.Query("code") | ||||||
|  |  | ||||||
| @@ -301,7 +301,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		log.Debug().Str("code", code).Msg("Got code") | 		log.Debug().Msg("Got code") | ||||||
|  |  | ||||||
| 		provider := api.Providers.GetProvider(providerName.Provider) | 		provider := api.Providers.GetProvider(providerName.Provider) | ||||||
|  |  | ||||||
| @@ -312,9 +312,9 @@ func (api *API) SetupRoutes() { | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		token, tokenErr := provider.ExchangeToken(code) | 		_, tokenErr := provider.ExchangeToken(code) | ||||||
|  |  | ||||||
| 		log.Debug().Str("token", token).Msg("Got token") | 		log.Debug().Msg("Got token") | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to exchange token", tokenErr) { | 		if handleApiError(c, "Failed to exchange token", tokenErr) { | ||||||
| 			return | 			return | ||||||
| @@ -363,7 +363,7 @@ func (api *API) SetupRoutes() { | |||||||
| 			RedirectURI: redirectURI, | 			RedirectURI: redirectURI, | ||||||
| 		}) | 		}) | ||||||
|  |  | ||||||
| 		log.Debug().Interface("redirectQuery", redirectQuery).Msg("Got redirect query") | 		log.Debug().Msg("Got redirect query") | ||||||
|  |  | ||||||
| 		if handleApiError(c, "Failed to build query", redirectQueryErr) { | 		if handleApiError(c, "Failed to build query", redirectQueryErr) { | ||||||
| 			return | 			return | ||||||
|   | |||||||
| @@ -1 +1 @@ | |||||||
| v2.0.0 | v2.0.2 | ||||||
| @@ -50,7 +50,7 @@ func (auth *Auth) EmailWhitelisted(emailSrc string) bool { | |||||||
| func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) { | func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) { | ||||||
| 	log.Debug().Msg("Creating session cookie") | 	log.Debug().Msg("Creating session cookie") | ||||||
| 	sessions := sessions.Default(c) | 	sessions := sessions.Default(c) | ||||||
| 	log.Debug().Interface("data", data).Msg("Setting session cookie") | 	log.Debug().Msg("Setting session cookie") | ||||||
| 	sessions.Set("username", data.Username) | 	sessions.Set("username", data.Username) | ||||||
| 	sessions.Set("provider", data.Provider) | 	sessions.Set("provider", data.Provider) | ||||||
| 	sessions.Save() | 	sessions.Save() | ||||||
| @@ -70,13 +70,10 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) | |||||||
| 	cookieUsername := sessions.Get("username") | 	cookieUsername := sessions.Get("username") | ||||||
| 	cookieProvider := sessions.Get("provider") | 	cookieProvider := sessions.Get("provider") | ||||||
|  |  | ||||||
| 	log.Debug().Interface("cookieUsername", cookieUsername).Msg("Got username") |  | ||||||
| 	log.Debug().Interface("cookieProvider", cookieProvider).Msg("Got provider") |  | ||||||
|  |  | ||||||
| 	username, usernameOk := cookieUsername.(string) | 	username, usernameOk := cookieUsername.(string) | ||||||
| 	provider, providerOk := cookieProvider.(string) | 	provider, providerOk := cookieProvider.(string) | ||||||
|  |  | ||||||
| 	log.Debug().Str("username", username).Bool("usernameOk", usernameOk).Str("provider", provider).Bool("providerOk", providerOk).Msg("Parsed cookie") | 	log.Debug().Str("username", username).Str("provider", provider).Msg("Parsed cookie") | ||||||
|  |  | ||||||
| 	if !usernameOk || !providerOk { | 	if !usernameOk || !providerOk { | ||||||
| 		log.Warn().Msg("Session cookie invalid") | 		log.Warn().Msg("Session cookie invalid") | ||||||
|   | |||||||
| @@ -34,8 +34,6 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("cookie", cookie).Msg("Got session cookie") |  | ||||||
|  |  | ||||||
| 	if cookie.Provider == "username" { | 	if cookie.Provider == "username" { | ||||||
| 		log.Debug().Msg("Provider is username") | 		log.Debug().Msg("Provider is username") | ||||||
| 		if hooks.Auth.GetUser(cookie.Username) != nil { | 		if hooks.Auth.GetUser(cookie.Username) != nil { | ||||||
| @@ -55,7 +53,7 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | |||||||
| 	if provider != nil { | 	if provider != nil { | ||||||
| 		log.Debug().Msg("Provider exists") | 		log.Debug().Msg("Provider exists") | ||||||
| 		if !hooks.Auth.EmailWhitelisted(cookie.Username) { | 		if !hooks.Auth.EmailWhitelisted(cookie.Username) { | ||||||
| 			log.Error().Msgf("Email %s not whitelisted", cookie.Username) | 			log.Error().Str("email", cookie.Username).Msg("Email is not whitelisted") | ||||||
| 			hooks.Auth.DeleteSessionCookie(c) | 			hooks.Auth.DeleteSessionCookie(c) | ||||||
| 			return types.UserContext{ | 			return types.UserContext{ | ||||||
| 				Username:   "", | 				Username:   "", | ||||||
| @@ -73,7 +71,6 @@ func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Error().Msg("Provider does not exist") |  | ||||||
| 	return types.UserContext{ | 	return types.UserContext{ | ||||||
| 		Username:   "", | 		Username:   "", | ||||||
| 		IsLoggedIn: false, | 		IsLoggedIn: false, | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ func GetGenericEmail(client *http.Client, url string) (string, error) { | |||||||
| 		return "", jsonErr | 		return "", jsonErr | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("user", user).Msg("Parsed user from generic provider") | 	log.Debug().Msg("Parsed user from generic provider") | ||||||
|  |  | ||||||
| 	return user.Email, nil | 	return user.Email, nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -43,7 +43,7 @@ func GetGithubEmail(client *http.Client) (string, error) { | |||||||
| 		return "", jsonErr | 		return "", jsonErr | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("emails", emails).Msg("Parsed emails from github") | 	log.Debug().Msg("Parsed emails from github") | ||||||
|  |  | ||||||
| 	for _, email := range emails { | 	for _, email := range emails { | ||||||
| 		if email.Primary { | 		if email.Primary { | ||||||
|   | |||||||
| @@ -41,7 +41,7 @@ func GetGoogleEmail(client *http.Client) (string, error) { | |||||||
| 		return "", jsonErr | 		return "", jsonErr | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("user", user).Msg("Parsed user from google") | 	log.Debug().Msg("Parsed user from google") | ||||||
|  |  | ||||||
| 	return user.Email, nil | 	return user.Email, nil | ||||||
| } | } | ||||||
|   | |||||||
| @@ -19,8 +19,8 @@ type User struct { | |||||||
| type Users []User | type Users []User | ||||||
|  |  | ||||||
| type Config struct { | type Config struct { | ||||||
| 	Port                    int    `validate:"number" mapstructure:"port"` | 	Port                    int    `mapstructure:"port" validate:"required"` | ||||||
| 	Address                 string `mapstructure:"address, ip4_addr"` | 	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"` | ||||||
| @@ -43,7 +43,7 @@ type Config struct { | |||||||
| 	DisableContinue         bool   `mapstructure:"disable-continue"` | 	DisableContinue         bool   `mapstructure:"disable-continue"` | ||||||
| 	OAuthWhitelist          string `mapstructure:"oauth-whitelist"` | 	OAuthWhitelist          string `mapstructure:"oauth-whitelist"` | ||||||
| 	CookieExpiry            int    `mapstructure:"cookie-expiry"` | 	CookieExpiry            int    `mapstructure:"cookie-expiry"` | ||||||
| 	LogLevel                int8   `mapstructure:"log-level"` | 	LogLevel                int8   `mapstructure:"log-level" validate:"min=-1,max=5"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type UserContext struct { | type UserContext struct { | ||||||
|   | |||||||
| @@ -15,15 +15,12 @@ func ParseUsers(users string) (types.Users, error) { | |||||||
| 	var usersParsed types.Users | 	var usersParsed types.Users | ||||||
| 	userList := strings.Split(users, ",") | 	userList := strings.Split(users, ",") | ||||||
|  |  | ||||||
| 	log.Debug().Strs("users", userList).Msg("Splitted users") |  | ||||||
|  |  | ||||||
| 	if len(userList) == 0 { | 	if len(userList) == 0 { | ||||||
| 		return types.Users{}, errors.New("invalid user format") | 		return types.Users{}, errors.New("invalid user format") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	for _, user := range userList { | 	for _, user := range userList { | ||||||
| 		userSplit := strings.Split(user, ":") | 		userSplit := strings.Split(user, ":") | ||||||
| 		log.Debug().Strs("user", userSplit).Msg("Splitting user") |  | ||||||
| 		if len(userSplit) != 2 { | 		if len(userSplit) != 2 { | ||||||
| 			return types.Users{}, errors.New("invalid user format") | 			return types.Users{}, errors.New("invalid user format") | ||||||
| 		} | 		} | ||||||
| @@ -33,7 +30,7 @@ func ParseUsers(users string) (types.Users, error) { | |||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Interface("users", usersParsed).Msg("Parsed users") | 	log.Debug().Msg("Parsed users") | ||||||
|  |  | ||||||
| 	return usersParsed, nil | 	return usersParsed, nil | ||||||
| } | } | ||||||
| @@ -77,21 +74,19 @@ func ParseFileToLine(content string) string { | |||||||
| 			continue | 			continue | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		users = append(users, line) | 		users = append(users, strings.TrimSpace(line)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	return strings.Join(users, ",") | 	return strings.Join(users, ",") | ||||||
| } | } | ||||||
|  |  | ||||||
| func GetSecret(env string, file string) string { | func GetSecret(conf string, file string) string { | ||||||
| 	if env == "" && file == "" { | 	if conf == "" && file == "" { | ||||||
| 		log.Debug().Msg("No secret provided") |  | ||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if env != "" { | 	if conf != "" { | ||||||
| 		log.Debug().Str("secret", env).Msg("Using secret from env") | 		return conf | ||||||
| 		return env |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	contents, err := ReadFile(file) | 	contents, err := ReadFile(file) | ||||||
| @@ -100,29 +95,29 @@ func GetSecret(env string, file string) string { | |||||||
| 		return "" | 		return "" | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Str("secret", contents).Msg("Using secret from file") |  | ||||||
|  |  | ||||||
| 	return contents | 	return contents | ||||||
| } | } | ||||||
|  |  | ||||||
| func GetUsers(env string, file string) (types.Users, error) { | func GetUsers(conf string, file string) (types.Users, error) { | ||||||
| 	var users string | 	var users string | ||||||
|  |  | ||||||
| 	if env == "" && file == "" { | 	if conf == "" && file == "" { | ||||||
| 		return types.Users{}, errors.New("no users provided") | 		return types.Users{}, errors.New("no users provided") | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if env != "" { | 	if conf != "" { | ||||||
| 		log.Debug().Str("users", env).Msg("Using users from env") | 		log.Debug().Msg("Using users from config") | ||||||
| 		users += env | 		users += conf | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	if file != "" { | 	if file != "" { | ||||||
| 		fileContents, fileErr := ReadFile(file) | 		fileContents, fileErr := ReadFile(file) | ||||||
|  |  | ||||||
| 		if fileErr == nil { | 		if fileErr == nil { | ||||||
| 			log.Debug().Str("users", ParseFileToLine(fileContents)).Msg("Using users from file") | 			log.Debug().Msg("Using users from file") | ||||||
| 			users += "," | 			if users != "" { | ||||||
|  | 				users += "," | ||||||
|  | 			} | ||||||
| 			users += ParseFileToLine(fileContents) | 			users += ParseFileToLine(fileContents) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								main.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								main.go
									
									
									
									
									
								
							| @@ -4,7 +4,6 @@ import ( | |||||||
| 	"os" | 	"os" | ||||||
| 	"time" | 	"time" | ||||||
| 	"tinyauth/cmd" | 	"tinyauth/cmd" | ||||||
| 	"tinyauth/internal/assets" |  | ||||||
|  |  | ||||||
| 	"github.com/rs/zerolog" | 	"github.com/rs/zerolog" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| @@ -12,8 +11,7 @@ import ( | |||||||
|  |  | ||||||
| func main() { | func main() { | ||||||
| 	// Logger | 	// Logger | ||||||
| 	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger() | 	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).With().Timestamp().Logger().Level(zerolog.FatalLevel) | ||||||
| 	log.Info().Str("version", assets.Version).Msg("Starting tinyauth") |  | ||||||
|  |  | ||||||
| 	// Run cmd | 	// Run cmd | ||||||
| 	cmd.Execute() | 	cmd.Execute() | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								site/bun.lockb
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								site/bun.lockb
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -1,8 +1,9 @@ | |||||||
| import { Button, Paper, Text } from "@mantine/core"; | import { Button, Code, Paper, Text } from "@mantine/core"; | ||||||
| import { notifications } from "@mantine/notifications"; | import { notifications } from "@mantine/notifications"; | ||||||
| import { Navigate } from "react-router"; | import { Navigate } from "react-router"; | ||||||
| import { useUserContext } from "../context/user-context"; | import { useUserContext } from "../context/user-context"; | ||||||
| import { Layout } from "../components/layouts/layout"; | import { Layout } from "../components/layouts/layout"; | ||||||
|  | import { ReactNode } from "react"; | ||||||
|  |  | ||||||
| export const ContinuePage = () => { | export const ContinuePage = () => { | ||||||
|   const queryString = window.location.search; |   const queryString = window.location.search; | ||||||
| @@ -12,11 +13,11 @@ export const ContinuePage = () => { | |||||||
|   const { isLoggedIn, disableContinue } = useUserContext(); |   const { isLoggedIn, disableContinue } = useUserContext(); | ||||||
|  |  | ||||||
|   if (!isLoggedIn) { |   if (!isLoggedIn) { | ||||||
|     return <Navigate to="/login" />; |     return <Navigate to={`/login?redirect_uri=${redirectUri}`} />; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   if (disableContinue && redirectUri !== "null") { |   if (redirectUri === "null") { | ||||||
|     window.location.replace(redirectUri!); |     return <Navigate to="/" />; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   const redirect = () => { |   const redirect = () => { | ||||||
| @@ -26,31 +27,62 @@ export const ContinuePage = () => { | |||||||
|       color: "blue", |       color: "blue", | ||||||
|     }); |     }); | ||||||
|     setTimeout(() => { |     setTimeout(() => { | ||||||
|       window.location.replace(redirectUri!); |       window.location.href = redirectUri!; | ||||||
|     }, 500); |     }, 500); | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|  |   const urlParsed = URL.parse(redirectUri!); | ||||||
|  |  | ||||||
|  |   if ( | ||||||
|  |     window.location.protocol === "https:" && | ||||||
|  |     urlParsed!.protocol === "http:" | ||||||
|  |   ) { | ||||||
|  |     return ( | ||||||
|  |       <ContinuePageLayout> | ||||||
|  |         <Text size="xl" fw={700}> | ||||||
|  |           Insecure Redirect | ||||||
|  |         </Text> | ||||||
|  |         <Text> | ||||||
|  |           Your are logged in but trying to redirect from <Code>https</Code> to{" "} | ||||||
|  |           <Code>http</Code>, please click the button to redirect. | ||||||
|  |         </Text> | ||||||
|  |         <Button fullWidth mt="xl" onClick={redirect}> | ||||||
|  |           Continue | ||||||
|  |         </Button> | ||||||
|  |       </ContinuePageLayout> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (disableContinue) { | ||||||
|  |     window.location.href = redirectUri!; | ||||||
|  |     return ( | ||||||
|  |       <ContinuePageLayout> | ||||||
|  |         <Text size="xl" fw={700}> | ||||||
|  |           Redirecting | ||||||
|  |         </Text> | ||||||
|  |         <Text>You should be redirected to your app soon.</Text> | ||||||
|  |       </ContinuePageLayout> | ||||||
|  |     ); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return ( | ||||||
|  |     <ContinuePageLayout> | ||||||
|  |       <Text size="xl" fw={700}> | ||||||
|  |         Continue | ||||||
|  |       </Text> | ||||||
|  |       <Text>Click the button to continue to your app.</Text> | ||||||
|  |       <Button fullWidth mt="xl" onClick={redirect}> | ||||||
|  |         Continue | ||||||
|  |       </Button> | ||||||
|  |     </ContinuePageLayout> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | export const ContinuePageLayout = ({ children }: { children: ReactNode }) => { | ||||||
|   return ( |   return ( | ||||||
|     <Layout> |     <Layout> | ||||||
|       <Paper shadow="md" p={30} mt={30} radius="md" withBorder> |       <Paper shadow="md" p={30} mt={30} radius="md" withBorder> | ||||||
|         {redirectUri !== "null" ? ( |         {children} | ||||||
|           <> |  | ||||||
|             <Text size="xl" fw={700}> |  | ||||||
|               Continue |  | ||||||
|             </Text> |  | ||||||
|             <Text>Click the button to continue to your app.</Text> |  | ||||||
|             <Button fullWidth mt="xl" onClick={redirect}> |  | ||||||
|               Continue |  | ||||||
|             </Button> |  | ||||||
|           </> |  | ||||||
|         ) : ( |  | ||||||
|           <> |  | ||||||
|             <Text size="xl" fw={700}> |  | ||||||
|               Logged in |  | ||||||
|             </Text> |  | ||||||
|             <Text>You are now signed in and can use your apps.</Text> |  | ||||||
|           </> |  | ||||||
|         )} |  | ||||||
|       </Paper> |       </Paper> | ||||||
|     </Layout> |     </Layout> | ||||||
|   ); |   ); | ||||||
|   | |||||||
| @@ -65,8 +65,12 @@ export const LoginPage = () => { | |||||||
|         color: "green", |         color: "green", | ||||||
|       }); |       }); | ||||||
|       setTimeout(() => { |       setTimeout(() => { | ||||||
|         window.location.replace(`/continue?redirect_uri=${redirectUri}`); |         if (redirectUri === "null") { | ||||||
|       }); |           window.location.replace("/"); | ||||||
|  |         } else { | ||||||
|  |           window.location.replace(`/continue?redirect_uri=${redirectUri}`); | ||||||
|  |         } | ||||||
|  |       }, 500); | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
| @@ -84,7 +88,14 @@ export const LoginPage = () => { | |||||||
|       }); |       }); | ||||||
|     }, |     }, | ||||||
|     onSuccess: (data) => { |     onSuccess: (data) => { | ||||||
|       window.location.replace(data.data.url); |       notifications.show({ | ||||||
|  |         title: "Redirecting", | ||||||
|  |         message: "Redirecting to your OAuth provider", | ||||||
|  |         color: "blue", | ||||||
|  |       }); | ||||||
|  |       setTimeout(() => { | ||||||
|  |         window.location.href = data.data.url; | ||||||
|  |       }, 500); | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -32,7 +32,7 @@ export const LogoutPage = () => { | |||||||
|         color: "green", |         color: "green", | ||||||
|       }); |       }); | ||||||
|       setTimeout(() => { |       setTimeout(() => { | ||||||
|         window.location.reload(); |         window.location.replace("/login"); | ||||||
|       }, 500); |       }, 500); | ||||||
|     }, |     }, | ||||||
|   }); |   }); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user