package cmd import ( "os" "strings" cmd "tinyauth/cmd/user" "tinyauth/internal/api" "tinyauth/internal/auth" "tinyauth/internal/hooks" "tinyauth/internal/providers" "tinyauth/internal/types" "tinyauth/internal/utils" "github.com/go-playground/validator/v10" "github.com/rs/zerolog/log" "github.com/spf13/cobra" "github.com/spf13/viper" ) var rootCmd = &cobra.Command{ Use: "tinyauth", Short: "An extremely simple traefik forward auth proxy.", Long: `Tinyauth is an extremely simple traefik forward-auth login screen that makes securing your apps easy.`, Run: func(cmd *cobra.Command, args []string) { // Get config log.Info().Msg("Parsing config") var config types.Config parseErr := viper.Unmarshal(&config) HandleError(parseErr, "Failed to parse config") // Validate config log.Info().Msg("Validating config") validator := validator.New() validateErr := validator.Struct(config) HandleError(validateErr, "Invalid config") // Parse users log.Info().Msg("Parsing users") if config.UsersFile == "" && config.Users == "" { log.Fatal().Msg("No users provided") os.Exit(1) } usersString := config.Users if config.UsersFile != "" { log.Info().Msg("Reading users from file") usersFromFile, readErr := utils.GetUsersFromFile(config.UsersFile) HandleError(readErr, "Failed to read users from file") usersFromFileParsed := strings.Join(strings.Split(usersFromFile, "\n"), ",") if usersString != "" { usersString = usersString + "," + usersFromFileParsed } else { usersString = usersFromFileParsed } } users, parseErr := utils.ParseUsers(usersString) HandleError(parseErr, "Failed to parse users") // Create OAuth config oauthConfig := types.OAuthConfig{ GithubClientId: config.GithubClientId, GithubClientSecret: config.GithubClientSecret, GoogleClientId: config.GoogleClientId, GoogleClientSecret: config.GoogleClientSecret, MicrosoftClientId: config.MicrosoftClientId, MicrosoftClientSecret: config.MicrosoftClientSecret, } // Create auth service auth := auth.NewAuth(users) // Create OAuth providers service providers := providers.NewProviders(oauthConfig) // Initialize providers providers.Init() // Create hooks service hooks := hooks.NewHooks(auth, providers) // Create API api := api.NewAPI(types.APIConfig{ Port: config.Port, Address: config.Address, Secret: config.Secret, AppURL: config.AppURL, CookieSecure: config.CookieSecure, }, hooks, auth, providers) // Setup routes api.Init() api.SetupRoutes() // Start api.Run() }, } func Execute() { err := rootCmd.Execute() if err != nil { log.Fatal().Err(err).Msg("Failed to execute command") os.Exit(1) } } func HandleError(err error, msg string) { if err != nil { log.Fatal().Err(err).Msg(msg) os.Exit(1) } } func init() { rootCmd.AddCommand(cmd.UserCmd()) viper.AutomaticEnv() rootCmd.Flags().Int("port", 3000, "Port to run the server on.") rootCmd.Flags().String("address", "0.0.0.0", "Address to bind the server to.") rootCmd.Flags().String("secret", "", "Secret to use for the cookie.") rootCmd.Flags().String("app-url", "", "The tinyauth URL.") rootCmd.Flags().String("users", "", "Comma separated list of users in the format username:bcrypt-hashed-password.") rootCmd.Flags().String("users-file", "", "Path to a file containing users in the format username:bcrypt-hashed-password.") rootCmd.Flags().Bool("cookie-secure", false, "Send cookie over secure connection only.") rootCmd.Flags().String("github-client-id", "", "Github OAuth client ID.") rootCmd.Flags().String("github-client-secret", "", "Github OAuth client secret.") rootCmd.Flags().String("google-client-id", "", "Google OAuth client ID.") rootCmd.Flags().String("google-client-secret", "", "Google OAuth client secret.") rootCmd.Flags().String("microsoft-client-id", "", "Microsoft OAuth client ID.") rootCmd.Flags().String("microsoft-client-secret", "", "Microsoft OAuth client secret.") viper.BindEnv("port", "PORT") viper.BindEnv("address", "ADDRESS") viper.BindEnv("secret", "SECRET") viper.BindEnv("app-url", "APP_URL") viper.BindEnv("users", "USERS") viper.BindEnv("users-file", "USERS_FILE") viper.BindEnv("cookie-secure", "COOKIE_SECURE") viper.BindEnv("github-client-id", "GITHUB_CLIENT_ID") viper.BindEnv("github-client-secret", "GITHUB_CLIENT_SECRET") viper.BindEnv("google-client-id", "GOOGLE_CLIENT_ID") viper.BindEnv("google-client-secret", "GOOGLE_CLIENT_SECRET") viper.BindEnv("microsoft-client-id", "MICROSOFT_CLIENT_ID") viper.BindEnv("microsoft-client-secret", "MICROSOFT_CLIENT_SECRET") viper.BindPFlags(rootCmd.Flags()) }