mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-10-30 21:55:43 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			a629430a88
			...
			feat/multi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | df10eb65ce | ||
|   | 8bf5a6067e | 
							
								
								
									
										13
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								cmd/root.go
									
									
									
									
									
								
							| @@ -107,10 +107,6 @@ var rootCmd = &cobra.Command{ | |||||||
| 		apiConfig := types.APIConfig{ | 		apiConfig := types.APIConfig{ | ||||||
| 			Port:    config.Port, | 			Port:    config.Port, | ||||||
| 			Address: config.Address, | 			Address: config.Address, | ||||||
| 			Secret:        config.Secret, |  | ||||||
| 			CookieSecure:  config.CookieSecure, |  | ||||||
| 			SessionExpiry: config.SessionExpiry, |  | ||||||
| 			Domain:        domain, |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		// Create docker service | 		// Create docker service | ||||||
| @@ -121,7 +117,14 @@ var rootCmd = &cobra.Command{ | |||||||
| 		HandleError(err, "Failed to initialize docker") | 		HandleError(err, "Failed to initialize docker") | ||||||
|  |  | ||||||
| 		// Create auth service | 		// Create auth service | ||||||
| 		auth := auth.NewAuth(docker, users, oauthWhitelist, config.SessionExpiry) | 		auth := auth.NewAuth(types.AuthConfig{ | ||||||
|  | 			Domain:         domain, | ||||||
|  | 			Secret:         config.Secret, | ||||||
|  | 			SessionExpiry:  config.SessionExpiry, | ||||||
|  | 			CookieSecure:   config.CookieSecure, | ||||||
|  | 			Users:          users, | ||||||
|  | 			OAuthWhitelist: oauthWhitelist, | ||||||
|  | 		}, docker) | ||||||
|  |  | ||||||
| 		// Create OAuth providers service | 		// Create OAuth providers service | ||||||
| 		providers := providers.NewProviders(oauthConfig) | 		providers := providers.NewProviders(oauthConfig) | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,7 +3,6 @@ module tinyauth | |||||||
| go 1.23.2 | go 1.23.2 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	github.com/gin-contrib/sessions v1.0.2 |  | ||||||
| 	github.com/gin-gonic/gin v1.10.0 | 	github.com/gin-gonic/gin v1.10.0 | ||||||
| 	github.com/go-playground/validator/v10 v10.24.0 | 	github.com/go-playground/validator/v10 v10.24.0 | ||||||
| 	github.com/google/go-querystring v1.1.0 | 	github.com/google/go-querystring v1.1.0 | ||||||
| @@ -58,9 +57,8 @@ require ( | |||||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||||
| 	github.com/goccy/go-json v0.10.4 // indirect | 	github.com/goccy/go-json v0.10.4 // indirect | ||||||
| 	github.com/gogo/protobuf v1.3.2 // indirect | 	github.com/gogo/protobuf v1.3.2 // indirect | ||||||
| 	github.com/gorilla/context v1.1.2 // indirect |  | ||||||
| 	github.com/gorilla/securecookie v1.1.2 // indirect | 	github.com/gorilla/securecookie v1.1.2 // indirect | ||||||
| 	github.com/gorilla/sessions v1.2.2 // indirect | 	github.com/gorilla/sessions v1.2.2 | ||||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||||
| 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | 	github.com/inconshreveable/mousetrap v1.1.0 // indirect | ||||||
| 	github.com/json-iterator/go v1.1.12 // indirect | 	github.com/json-iterator/go v1.1.12 // indirect | ||||||
|   | |||||||
							
								
								
									
										4
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.sum
									
									
									
									
									
								
							| @@ -65,8 +65,6 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos | |||||||
| github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= | ||||||
| github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= | github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= | ||||||
| github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= | github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= | ||||||
| github.com/gin-contrib/sessions v1.0.2 h1:UaIjUvTH1cMeOdj3in6dl+Xb6It8RiKRF9Z1anbUyCA= |  | ||||||
| github.com/gin-contrib/sessions v1.0.2/go.mod h1:KxKxWqWP5LJVDCInulOl4WbLzK2KSPlLesfZ66wRvMs= |  | ||||||
| github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= | github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= | ||||||
| github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= | github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= | ||||||
| github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | ||||||
| @@ -99,8 +97,6 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | |||||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o= |  | ||||||
| github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM= |  | ||||||
| github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= | github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= | ||||||
| github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= | github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= | ||||||
| github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= | github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= | ||||||
|   | |||||||
| @@ -11,23 +11,21 @@ import ( | |||||||
| 	"tinyauth/internal/handlers" | 	"tinyauth/internal/handlers" | ||||||
| 	"tinyauth/internal/types" | 	"tinyauth/internal/types" | ||||||
|  |  | ||||||
| 	"github.com/gin-contrib/sessions" |  | ||||||
| 	"github.com/gin-contrib/sessions/cookie" |  | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API { | func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API { | ||||||
| 	return &API{ | 	return &API{ | ||||||
| 		Config:   config, |  | ||||||
| 		Handlers: handlers, | 		Handlers: handlers, | ||||||
|  | 		Config:   config, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type API struct { | type API struct { | ||||||
| 	Config   types.APIConfig |  | ||||||
| 	Router   *gin.Engine | 	Router   *gin.Engine | ||||||
| 	Handlers *handlers.Handlers | 	Handlers *handlers.Handlers | ||||||
|  | 	Config   types.APIConfig | ||||||
| } | } | ||||||
|  |  | ||||||
| func (api *API) Init() { | func (api *API) Init() { | ||||||
| @@ -51,21 +49,6 @@ func (api *API) Init() { | |||||||
| 	log.Debug().Msg("Setting up file server") | 	log.Debug().Msg("Setting up file server") | ||||||
| 	fileServer := http.FileServer(http.FS(dist)) | 	fileServer := http.FileServer(http.FS(dist)) | ||||||
|  |  | ||||||
| 	// Setup cookie store |  | ||||||
| 	log.Debug().Msg("Setting up cookie store") |  | ||||||
| 	store := cookie.NewStore([]byte(api.Config.Secret)) |  | ||||||
|  |  | ||||||
| 	// Use session middleware |  | ||||||
| 	store.Options(sessions.Options{ |  | ||||||
| 		Domain:   api.Config.Domain, |  | ||||||
| 		Path:     "/", |  | ||||||
| 		HttpOnly: true, |  | ||||||
| 		Secure:   api.Config.CookieSecure, |  | ||||||
| 		MaxAge:   api.Config.SessionExpiry, |  | ||||||
| 	}) |  | ||||||
|  |  | ||||||
| 	router.Use(sessions.Sessions("tinyauth", store)) |  | ||||||
|  |  | ||||||
| 	// UI middleware | 	// UI middleware | ||||||
| 	router.Use(func(c *gin.Context) { | 	router.Use(func(c *gin.Context) { | ||||||
| 		// If not an API request, serve the UI | 		// If not an API request, serve the UI | ||||||
|   | |||||||
| @@ -19,13 +19,16 @@ import ( | |||||||
| 	"github.com/magiconair/properties/assert" | 	"github.com/magiconair/properties/assert" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | // User | ||||||
|  | var User = types.User{ | ||||||
|  | 	Username: "user", | ||||||
|  | 	Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass | ||||||
|  | } | ||||||
|  |  | ||||||
| // Simple API config for tests | // Simple API config for tests | ||||||
| var apiConfig = types.APIConfig{ | var apiConfig = types.APIConfig{ | ||||||
| 	Port:    8080, | 	Port:    8080, | ||||||
| 	Address: "0.0.0.0", | 	Address: "0.0.0.0", | ||||||
| 	Secret:        "super-secret-api-thing-for-tests", // It is 32 chars long |  | ||||||
| 	CookieSecure:  false, |  | ||||||
| 	SessionExpiry: 3600, |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // Simple handlers config for tests | // Simple handlers config for tests | ||||||
| @@ -38,15 +41,21 @@ var handlersConfig = types.HandlersConfig{ | |||||||
| 	GenericName:     "Generic", | 	GenericName:     "Generic", | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Simple auth config for tests | ||||||
|  | var authConfig = types.AuthConfig{ | ||||||
|  | 	Domain:        "localhost", | ||||||
|  | 	Secret:        "super-secret-api-thing-for-tests", // It is 32 chars long | ||||||
|  | 	CookieSecure:  false, | ||||||
|  | 	SessionExpiry: 3600, | ||||||
|  | 	Users: types.Users{ | ||||||
|  | 		User, | ||||||
|  | 	}, | ||||||
|  | 	OAuthWhitelist: []string{}, | ||||||
|  | } | ||||||
|  |  | ||||||
| // Cookie | // Cookie | ||||||
| var cookie string | var cookie string | ||||||
|  |  | ||||||
| // User |  | ||||||
| var user = types.User{ |  | ||||||
| 	Username: "user", |  | ||||||
| 	Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // We need all this to be able to test the API | // We need all this to be able to test the API | ||||||
| func getAPI(t *testing.T) *api.API { | func getAPI(t *testing.T) *api.API { | ||||||
| 	// Create docker service | 	// Create docker service | ||||||
| @@ -61,12 +70,7 @@ func getAPI(t *testing.T) *api.API { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Create auth service | 	// Create auth service | ||||||
| 	auth := auth.NewAuth(docker, types.Users{ | 	auth := auth.NewAuth(authConfig, docker) | ||||||
| 		{ |  | ||||||
| 			Username: user.Username, |  | ||||||
| 			Password: user.Password, |  | ||||||
| 		}, |  | ||||||
| 	}, nil, apiConfig.SessionExpiry) |  | ||||||
|  |  | ||||||
| 	// Create providers service | 	// Create providers service | ||||||
| 	providers := providers.NewProviders(types.OAuthConfig{}) | 	providers := providers.NewProviders(types.OAuthConfig{}) | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| package auth | package auth | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| 	"slices" | 	"slices" | ||||||
| 	"strings" | 	"strings" | ||||||
| @@ -8,31 +9,27 @@ import ( | |||||||
| 	"tinyauth/internal/docker" | 	"tinyauth/internal/docker" | ||||||
| 	"tinyauth/internal/types" | 	"tinyauth/internal/types" | ||||||
|  |  | ||||||
| 	"github.com/gin-contrib/sessions" |  | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/gorilla/sessions" | ||||||
| 	"github.com/rs/zerolog/log" | 	"github.com/rs/zerolog/log" | ||||||
| 	"golang.org/x/crypto/bcrypt" | 	"golang.org/x/crypto/bcrypt" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func NewAuth(docker *docker.Docker, userList types.Users, oauthWhitelist []string, sessionExpiry int) *Auth { | func NewAuth(config types.AuthConfig, docker *docker.Docker) *Auth { | ||||||
| 	return &Auth{ | 	return &Auth{ | ||||||
| 		Docker: docker, | 		Docker: docker, | ||||||
| 		Users:          userList, | 		Config: config, | ||||||
| 		OAuthWhitelist: oauthWhitelist, |  | ||||||
| 		SessionExpiry:  sessionExpiry, |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type Auth struct { | type Auth struct { | ||||||
| 	Users          types.Users |  | ||||||
| 	Docker *docker.Docker | 	Docker *docker.Docker | ||||||
| 	OAuthWhitelist []string | 	Config types.AuthConfig | ||||||
| 	SessionExpiry  int |  | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) GetUser(username string) *types.User { | func (auth *Auth) GetUser(username string) *types.User { | ||||||
| 	// Loop through users and return the user if the username matches | 	// Loop through users and return the user if the username matches | ||||||
| 	for _, user := range auth.Users { | 	for _, user := range auth.Config.Users { | ||||||
| 		if user.Username == username { | 		if user.Username == username { | ||||||
| 			return &user | 			return &user | ||||||
| 		} | 		} | ||||||
| @@ -47,12 +44,12 @@ func (auth *Auth) CheckPassword(user types.User, password string) bool { | |||||||
|  |  | ||||||
| func (auth *Auth) EmailWhitelisted(emailSrc string) bool { | func (auth *Auth) EmailWhitelisted(emailSrc string) bool { | ||||||
| 	// If the whitelist is empty, allow all emails | 	// If the whitelist is empty, allow all emails | ||||||
| 	if len(auth.OAuthWhitelist) == 0 { | 	if len(auth.Config.OAuthWhitelist) == 0 { | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Loop through the whitelist and return true if the email matches | 	// Loop through the whitelist and return true if the email matches | ||||||
| 	for _, email := range auth.OAuthWhitelist { | 	for _, email := range auth.Config.OAuthWhitelist { | ||||||
| 		if email == emailSrc { | 		if email == emailSrc { | ||||||
| 			return true | 			return true | ||||||
| 		} | 		} | ||||||
| @@ -62,11 +59,35 @@ func (auth *Auth) EmailWhitelisted(emailSrc string) bool { | |||||||
| 	return false | 	return false | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) { | func (auth *Auth) GetCookieStore() *sessions.CookieStore { | ||||||
|  | 	// Create a new cookie store | ||||||
|  | 	store := sessions.NewCookieStore([]byte(auth.Config.Secret)) | ||||||
|  |  | ||||||
|  | 	// Configure the cookie store | ||||||
|  | 	store.Options = &sessions.Options{ | ||||||
|  | 		Path:     "/", | ||||||
|  | 		Domain:   fmt.Sprintf(".%s", auth.Config.Domain), | ||||||
|  | 		Secure:   auth.Config.CookieSecure, | ||||||
|  | 		MaxAge:   auth.Config.SessionExpiry, | ||||||
|  | 		HttpOnly: true, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Set the cookie store | ||||||
|  | 	return store | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) error { | ||||||
| 	log.Debug().Msg("Creating session cookie") | 	log.Debug().Msg("Creating session cookie") | ||||||
|  |  | ||||||
|  | 	// Get cookie store | ||||||
|  | 	store := auth.GetCookieStore() | ||||||
|  |  | ||||||
| 	// Get session | 	// Get session | ||||||
| 	sessions := sessions.Default(c) | 	sessions, err := store.Get(c.Request, "tinyauth") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Msg("Setting session cookie") | 	log.Debug().Msg("Setting session cookie") | ||||||
|  |  | ||||||
| @@ -76,43 +97,73 @@ func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie) | |||||||
| 	if data.TotpPending { | 	if data.TotpPending { | ||||||
| 		sessionExpiry = 3600 | 		sessionExpiry = 3600 | ||||||
| 	} else { | 	} else { | ||||||
| 		sessionExpiry = auth.SessionExpiry | 		sessionExpiry = auth.Config.SessionExpiry | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Set data | 	// Set data | ||||||
| 	sessions.Set("username", data.Username) | 	sessions.Values["username"] = data.Username | ||||||
| 	sessions.Set("provider", data.Provider) | 	sessions.Values["provider"] = data.Provider | ||||||
| 	sessions.Set("expiry", time.Now().Add(time.Duration(sessionExpiry)*time.Second).Unix()) | 	sessions.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix() | ||||||
| 	sessions.Set("totpPending", data.TotpPending) | 	sessions.Values["totpPending"] = data.TotpPending | ||||||
|  |  | ||||||
| 	// Save session | 	// Save session | ||||||
| 	sessions.Save() | 	err = sessions.Save(c.Request, c.Writer) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| func (auth *Auth) DeleteSessionCookie(c *gin.Context) { | 	// Return nil | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (auth *Auth) DeleteSessionCookie(c *gin.Context) error { | ||||||
| 	log.Debug().Msg("Deleting session cookie") | 	log.Debug().Msg("Deleting session cookie") | ||||||
|  |  | ||||||
|  | 	// Get cookie store | ||||||
|  | 	store := auth.GetCookieStore() | ||||||
|  |  | ||||||
| 	// Get session | 	// Get session | ||||||
| 	sessions := sessions.Default(c) | 	sessions, err := store.Get(c.Request, "tinyauth") | ||||||
|  |  | ||||||
| 	// Clear session | 	if err != nil { | ||||||
| 	sessions.Clear() | 		return err | ||||||
|  |  | ||||||
| 	// Save session |  | ||||||
| 	sessions.Save() |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie { | 	// Clear session | ||||||
|  | 	for key := range sessions.Values { | ||||||
|  | 		delete(sessions.Values, key) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Save session | ||||||
|  | 	err = sessions.Save(c.Request, c.Writer) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Return nil | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) { | ||||||
| 	log.Debug().Msg("Getting session cookie") | 	log.Debug().Msg("Getting session cookie") | ||||||
|  |  | ||||||
|  | 	// Get cookie store | ||||||
|  | 	store := auth.GetCookieStore() | ||||||
|  |  | ||||||
| 	// Get session | 	// Get session | ||||||
| 	sessions := sessions.Default(c) | 	sessions, err := store.Get(c.Request, "tinyauth") | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		return types.SessionCookie{}, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	// Get data | 	// Get data | ||||||
| 	cookieUsername := sessions.Get("username") | 	cookieUsername := sessions.Values["username"] | ||||||
| 	cookieProvider := sessions.Get("provider") | 	cookieProvider := sessions.Values["provider"] | ||||||
| 	cookieExpiry := sessions.Get("expiry") | 	cookieExpiry := sessions.Values["expiry"] | ||||||
| 	cookieTotpPending := sessions.Get("totpPending") | 	cookieTotpPending := sessions.Values["totpPending"] | ||||||
|  |  | ||||||
| 	// Convert interfaces to correct types | 	// Convert interfaces to correct types | ||||||
| 	username, usernameOk := cookieUsername.(string) | 	username, usernameOk := cookieUsername.(string) | ||||||
| @@ -123,7 +174,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie { | |||||||
| 	// Check if the cookie is invalid | 	// Check if the cookie is invalid | ||||||
| 	if !usernameOk || !providerOk || !expiryOk || !totpPendingOk { | 	if !usernameOk || !providerOk || !expiryOk || !totpPendingOk { | ||||||
| 		log.Warn().Msg("Session cookie invalid") | 		log.Warn().Msg("Session cookie invalid") | ||||||
| 		return types.SessionCookie{} | 		return types.SessionCookie{}, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Check if the cookie has expired | 	// Check if the cookie has expired | ||||||
| @@ -134,7 +185,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie { | |||||||
| 		auth.DeleteSessionCookie(c) | 		auth.DeleteSessionCookie(c) | ||||||
|  |  | ||||||
| 		// Return empty cookie | 		// Return empty cookie | ||||||
| 		return types.SessionCookie{} | 		return types.SessionCookie{}, nil | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Msg("Parsed cookie") | 	log.Debug().Str("username", username).Str("provider", provider).Int64("expiry", expiry).Bool("totpPending", totpPending).Msg("Parsed cookie") | ||||||
| @@ -144,12 +195,12 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie { | |||||||
| 		Username:    username, | 		Username:    username, | ||||||
| 		Provider:    provider, | 		Provider:    provider, | ||||||
| 		TotpPending: totpPending, | 		TotpPending: totpPending, | ||||||
| 	} | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) UserAuthConfigured() bool { | func (auth *Auth) UserAuthConfigured() bool { | ||||||
| 	// If there are users, return true | 	// If there are users, return true | ||||||
| 	return len(auth.Users) > 0 | 	return len(auth.Config.Users) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
| func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bool, error) { | func (auth *Auth) ResourceAllowed(c *gin.Context, context types.UserContext) (bool, error) { | ||||||
|   | |||||||
| @@ -19,20 +19,20 @@ import ( | |||||||
|  |  | ||||||
| func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers { | func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers { | ||||||
| 	return &Handlers{ | 	return &Handlers{ | ||||||
| 		Config:    config, |  | ||||||
| 		Auth:      auth, | 		Auth:      auth, | ||||||
| 		Hooks:     hooks, | 		Hooks:     hooks, | ||||||
| 		Providers: providers, | 		Providers: providers, | ||||||
| 		Docker:    docker, | 		Docker:    docker, | ||||||
|  | 		Config:    config, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| type Handlers struct { | type Handlers struct { | ||||||
| 	Config    types.HandlersConfig |  | ||||||
| 	Auth      *auth.Auth | 	Auth      *auth.Auth | ||||||
| 	Hooks     *hooks.Hooks | 	Hooks     *hooks.Hooks | ||||||
| 	Providers *providers.Providers | 	Providers *providers.Providers | ||||||
| 	Docker    *docker.Docker | 	Docker    *docker.Docker | ||||||
|  | 	Config    types.HandlersConfig | ||||||
| } | } | ||||||
|  |  | ||||||
| func (h *Handlers) AuthHandler(c *gin.Context) { | func (h *Handlers) AuthHandler(c *gin.Context) { | ||||||
|   | |||||||
| @@ -23,7 +23,13 @@ type Hooks struct { | |||||||
|  |  | ||||||
| func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext { | ||||||
| 	// Get session cookie and basic auth | 	// Get session cookie and basic auth | ||||||
| 	cookie := hooks.Auth.GetSessionCookie(c) | 	cookie, err := hooks.Auth.GetSessionCookie(c) | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error().Err(err).Msg("Failed to get session cookie") | ||||||
|  | 		return types.UserContext{} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	basic := hooks.Auth.GetBasicAuth(c) | 	basic := hooks.Auth.GetBasicAuth(c) | ||||||
|  |  | ||||||
| 	// Check if basic auth is set | 	// Check if basic auth is set | ||||||
|   | |||||||
| @@ -14,10 +14,10 @@ func NewOAuth(config oauth2.Config) *OAuth { | |||||||
| } | } | ||||||
|  |  | ||||||
| type OAuth struct { | type OAuth struct { | ||||||
| 	Config   oauth2.Config | 	Verifier string | ||||||
| 	Context  context.Context | 	Context  context.Context | ||||||
| 	Token    *oauth2.Token | 	Token    *oauth2.Token | ||||||
| 	Verifier string | 	Config   oauth2.Config | ||||||
| } | } | ||||||
|  |  | ||||||
| func (oauth *OAuth) Init() { | func (oauth *OAuth) Init() { | ||||||
|   | |||||||
| @@ -17,11 +17,11 @@ func NewProviders(config types.OAuthConfig) *Providers { | |||||||
| } | } | ||||||
|  |  | ||||||
| type Providers struct { | type Providers struct { | ||||||
| 	Config    types.OAuthConfig |  | ||||||
| 	Github    *oauth.OAuth | 	Github    *oauth.OAuth | ||||||
| 	Google    *oauth.OAuth | 	Google    *oauth.OAuth | ||||||
| 	Tailscale *oauth.OAuth | 	Tailscale *oauth.OAuth | ||||||
| 	Generic   *oauth.OAuth | 	Generic   *oauth.OAuth | ||||||
|  | 	Config    types.OAuthConfig | ||||||
| } | } | ||||||
|  |  | ||||||
| func (providers *Providers) Init() { | func (providers *Providers) Init() { | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								internal/types/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/types/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | |||||||
|  | package types | ||||||
|  |  | ||||||
|  | // LoginQuery is the query parameters for the login endpoint | ||||||
|  | type LoginQuery struct { | ||||||
|  | 	RedirectURI string `url:"redirect_uri"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // LoginRequest is the request body for the login endpoint | ||||||
|  | type LoginRequest struct { | ||||||
|  | 	Username string `json:"username"` | ||||||
|  | 	Password string `json:"password"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OAuthRequest is the request for the OAuth endpoint | ||||||
|  | type OAuthRequest struct { | ||||||
|  | 	Provider string `uri:"provider" binding:"required"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // UnauthorizedQuery is the query parameters for the unauthorized endpoint | ||||||
|  | type UnauthorizedQuery struct { | ||||||
|  | 	Username string `url:"username"` | ||||||
|  | 	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 | ||||||
|  | type Proxy struct { | ||||||
|  | 	Proxy string `uri:"proxy" binding:"required"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // User Context response is the response for the user context endpoint | ||||||
|  | type UserContextResponse struct { | ||||||
|  | 	Status      int    `json:"status"` | ||||||
|  | 	Message     string `json:"message"` | ||||||
|  | 	IsLoggedIn  bool   `json:"isLoggedIn"` | ||||||
|  | 	Username    string `json:"username"` | ||||||
|  | 	Provider    string `json:"provider"` | ||||||
|  | 	Oauth       bool   `json:"oauth"` | ||||||
|  | 	TotpPending bool   `json:"totpPending"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Totp request is the request for the totp endpoint | ||||||
|  | type TotpRequest struct { | ||||||
|  | 	Code string `json:"code"` | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								internal/types/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								internal/types/config.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | package types | ||||||
|  |  | ||||||
|  | // Config is the configuration for the tinyauth server | ||||||
|  | type Config struct { | ||||||
|  | 	Port                      int    `mapstructure:"port" validate:"required"` | ||||||
|  | 	Address                   string `validate:"required,ip4_addr" mapstructure:"address"` | ||||||
|  | 	Secret                    string `validate:"required,len=32" mapstructure:"secret"` | ||||||
|  | 	SecretFile                string `mapstructure:"secret-file"` | ||||||
|  | 	AppURL                    string `validate:"required,url" mapstructure:"app-url"` | ||||||
|  | 	Users                     string `mapstructure:"users"` | ||||||
|  | 	UsersFile                 string `mapstructure:"users-file"` | ||||||
|  | 	CookieSecure              bool   `mapstructure:"cookie-secure"` | ||||||
|  | 	GithubClientId            string `mapstructure:"github-client-id"` | ||||||
|  | 	GithubClientSecret        string `mapstructure:"github-client-secret"` | ||||||
|  | 	GithubClientSecretFile    string `mapstructure:"github-client-secret-file"` | ||||||
|  | 	GoogleClientId            string `mapstructure:"google-client-id"` | ||||||
|  | 	GoogleClientSecret        string `mapstructure:"google-client-secret"` | ||||||
|  | 	GoogleClientSecretFile    string `mapstructure:"google-client-secret-file"` | ||||||
|  | 	TailscaleClientId         string `mapstructure:"tailscale-client-id"` | ||||||
|  | 	TailscaleClientSecret     string `mapstructure:"tailscale-client-secret"` | ||||||
|  | 	TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"` | ||||||
|  | 	GenericClientId           string `mapstructure:"generic-client-id"` | ||||||
|  | 	GenericClientSecret       string `mapstructure:"generic-client-secret"` | ||||||
|  | 	GenericClientSecretFile   string `mapstructure:"generic-client-secret-file"` | ||||||
|  | 	GenericScopes             string `mapstructure:"generic-scopes"` | ||||||
|  | 	GenericAuthURL            string `mapstructure:"generic-auth-url"` | ||||||
|  | 	GenericTokenURL           string `mapstructure:"generic-token-url"` | ||||||
|  | 	GenericUserURL            string `mapstructure:"generic-user-url"` | ||||||
|  | 	GenericName               string `mapstructure:"generic-name"` | ||||||
|  | 	DisableContinue           bool   `mapstructure:"disable-continue"` | ||||||
|  | 	OAuthWhitelist            string `mapstructure:"oauth-whitelist"` | ||||||
|  | 	SessionExpiry             int    `mapstructure:"session-expiry"` | ||||||
|  | 	LogLevel                  int8   `mapstructure:"log-level" validate:"min=-1,max=5"` | ||||||
|  | 	Title                     string `mapstructure:"app-title"` | ||||||
|  | 	EnvFile                   string `mapstructure:"env-file"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // APIConfig is the configuration for the API | ||||||
|  | type APIConfig struct { | ||||||
|  | 	Port    int | ||||||
|  | 	Address string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OAuthConfig is the configuration for the providers | ||||||
|  | type OAuthConfig struct { | ||||||
|  | 	GithubClientId        string | ||||||
|  | 	GithubClientSecret    string | ||||||
|  | 	GoogleClientId        string | ||||||
|  | 	GoogleClientSecret    string | ||||||
|  | 	TailscaleClientId     string | ||||||
|  | 	TailscaleClientSecret string | ||||||
|  | 	GenericClientId       string | ||||||
|  | 	GenericClientSecret   string | ||||||
|  | 	GenericScopes         []string | ||||||
|  | 	GenericAuthURL        string | ||||||
|  | 	GenericTokenURL       string | ||||||
|  | 	GenericUserURL        string | ||||||
|  | 	AppURL                string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Server configuration | ||||||
|  | type HandlersConfig struct { | ||||||
|  | 	AppURL          string | ||||||
|  | 	Domain          string | ||||||
|  | 	CookieSecure    bool | ||||||
|  | 	DisableContinue bool | ||||||
|  | 	GenericName     string | ||||||
|  | 	Title           string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Auth configuration | ||||||
|  | type AuthConfig struct { | ||||||
|  | 	Domain         string | ||||||
|  | 	Secret         string | ||||||
|  | 	CookieSecure   bool | ||||||
|  | 	SessionExpiry  int | ||||||
|  | 	Users          Users | ||||||
|  | 	OAuthWhitelist []string | ||||||
|  | } | ||||||
| @@ -2,17 +2,6 @@ package types | |||||||
|  |  | ||||||
| import "tinyauth/internal/oauth" | import "tinyauth/internal/oauth" | ||||||
|  |  | ||||||
| // LoginQuery is the query parameters for the login endpoint |  | ||||||
| type LoginQuery struct { |  | ||||||
| 	RedirectURI string `url:"redirect_uri"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // LoginRequest is the request body for the login endpoint |  | ||||||
| type LoginRequest struct { |  | ||||||
| 	Username string `json:"username"` |  | ||||||
| 	Password string `json:"password"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // User is the struct for a user | // User is the struct for a user | ||||||
| type User struct { | type User struct { | ||||||
| 	Username   string | 	Username   string | ||||||
| @@ -23,82 +12,6 @@ type User struct { | |||||||
| // Users is a list of users | // Users is a list of users | ||||||
| type Users []User | type Users []User | ||||||
|  |  | ||||||
| // Config is the configuration for the tinyauth server |  | ||||||
| type Config struct { |  | ||||||
| 	Port                      int    `mapstructure:"port" validate:"required"` |  | ||||||
| 	Address                   string `validate:"required,ip4_addr" mapstructure:"address"` |  | ||||||
| 	Secret                    string `validate:"required,len=32" mapstructure:"secret"` |  | ||||||
| 	SecretFile                string `mapstructure:"secret-file"` |  | ||||||
| 	AppURL                    string `validate:"required,url" mapstructure:"app-url"` |  | ||||||
| 	Users                     string `mapstructure:"users"` |  | ||||||
| 	UsersFile                 string `mapstructure:"users-file"` |  | ||||||
| 	CookieSecure              bool   `mapstructure:"cookie-secure"` |  | ||||||
| 	GithubClientId            string `mapstructure:"github-client-id"` |  | ||||||
| 	GithubClientSecret        string `mapstructure:"github-client-secret"` |  | ||||||
| 	GithubClientSecretFile    string `mapstructure:"github-client-secret-file"` |  | ||||||
| 	GoogleClientId            string `mapstructure:"google-client-id"` |  | ||||||
| 	GoogleClientSecret        string `mapstructure:"google-client-secret"` |  | ||||||
| 	GoogleClientSecretFile    string `mapstructure:"google-client-secret-file"` |  | ||||||
| 	TailscaleClientId         string `mapstructure:"tailscale-client-id"` |  | ||||||
| 	TailscaleClientSecret     string `mapstructure:"tailscale-client-secret"` |  | ||||||
| 	TailscaleClientSecretFile string `mapstructure:"tailscale-client-secret-file"` |  | ||||||
| 	GenericClientId           string `mapstructure:"generic-client-id"` |  | ||||||
| 	GenericClientSecret       string `mapstructure:"generic-client-secret"` |  | ||||||
| 	GenericClientSecretFile   string `mapstructure:"generic-client-secret-file"` |  | ||||||
| 	GenericScopes             string `mapstructure:"generic-scopes"` |  | ||||||
| 	GenericAuthURL            string `mapstructure:"generic-auth-url"` |  | ||||||
| 	GenericTokenURL           string `mapstructure:"generic-token-url"` |  | ||||||
| 	GenericUserURL            string `mapstructure:"generic-user-url"` |  | ||||||
| 	GenericName               string `mapstructure:"generic-name"` |  | ||||||
| 	DisableContinue           bool   `mapstructure:"disable-continue"` |  | ||||||
| 	OAuthWhitelist            string `mapstructure:"oauth-whitelist"` |  | ||||||
| 	SessionExpiry             int    `mapstructure:"session-expiry"` |  | ||||||
| 	LogLevel                  int8   `mapstructure:"log-level" validate:"min=-1,max=5"` |  | ||||||
| 	Title                     string `mapstructure:"app-title"` |  | ||||||
| 	EnvFile                   string `mapstructure:"env-file"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // UserContext is the context for the user |  | ||||||
| type UserContext struct { |  | ||||||
| 	Username    string |  | ||||||
| 	IsLoggedIn  bool |  | ||||||
| 	OAuth       bool |  | ||||||
| 	Provider    string |  | ||||||
| 	TotpPending bool |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // APIConfig is the configuration for the API |  | ||||||
| type APIConfig struct { |  | ||||||
| 	Port          int |  | ||||||
| 	Address       string |  | ||||||
| 	Secret        string |  | ||||||
| 	CookieSecure  bool |  | ||||||
| 	SessionExpiry int |  | ||||||
| 	Domain        string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OAuthConfig is the configuration for the providers |  | ||||||
| type OAuthConfig struct { |  | ||||||
| 	GithubClientId        string |  | ||||||
| 	GithubClientSecret    string |  | ||||||
| 	GoogleClientId        string |  | ||||||
| 	GoogleClientSecret    string |  | ||||||
| 	TailscaleClientId     string |  | ||||||
| 	TailscaleClientSecret string |  | ||||||
| 	GenericClientId       string |  | ||||||
| 	GenericClientSecret   string |  | ||||||
| 	GenericScopes         []string |  | ||||||
| 	GenericAuthURL        string |  | ||||||
| 	GenericTokenURL       string |  | ||||||
| 	GenericUserURL        string |  | ||||||
| 	AppURL                string |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OAuthRequest is the request for the OAuth endpoint |  | ||||||
| type OAuthRequest struct { |  | ||||||
| 	Provider string `uri:"provider" binding:"required"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // OAuthProviders is the struct for the OAuth providers | // OAuthProviders is the struct for the OAuth providers | ||||||
| type OAuthProviders struct { | type OAuthProviders struct { | ||||||
| 	Github    *oauth.OAuth | 	Github    *oauth.OAuth | ||||||
| @@ -106,12 +19,6 @@ type OAuthProviders struct { | |||||||
| 	Microsoft *oauth.OAuth | 	Microsoft *oauth.OAuth | ||||||
| } | } | ||||||
|  |  | ||||||
| // UnauthorizedQuery is the query parameters for the unauthorized endpoint |  | ||||||
| type UnauthorizedQuery struct { |  | ||||||
| 	Username string `url:"username"` |  | ||||||
| 	Resource string `url:"resource"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SessionCookie is the cookie for the session (exculding the expiry) | // SessionCookie is the cookie for the session (exculding the expiry) | ||||||
| type SessionCookie struct { | type SessionCookie struct { | ||||||
| 	Username    string | 	Username    string | ||||||
| @@ -127,25 +34,13 @@ type TinyauthLabels struct { | |||||||
| 	Headers        map[string]string | 	Headers        map[string]string | ||||||
| } | } | ||||||
|  |  | ||||||
| // TailscaleQuery is the query parameters for the tailscale endpoint | // UserContext is the context for the user | ||||||
| type TailscaleQuery struct { | type UserContext struct { | ||||||
| 	Code int `url:"code"` | 	Username    string | ||||||
| } | 	IsLoggedIn  bool | ||||||
|  | 	OAuth       bool | ||||||
| // Proxy is the uri parameters for the proxy endpoint | 	Provider    string | ||||||
| type Proxy struct { | 	TotpPending bool | ||||||
| 	Proxy string `uri:"proxy" binding:"required"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // User Context response is the response for the user context endpoint |  | ||||||
| type UserContextResponse struct { |  | ||||||
| 	Status      int    `json:"status"` |  | ||||||
| 	Message     string `json:"message"` |  | ||||||
| 	IsLoggedIn  bool   `json:"isLoggedIn"` |  | ||||||
| 	Username    string `json:"username"` |  | ||||||
| 	Provider    string `json:"provider"` |  | ||||||
| 	Oauth       bool   `json:"oauth"` |  | ||||||
| 	TotpPending bool   `json:"totpPending"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // App Context is the response for the app context endpoint | // App Context is the response for the app context endpoint | ||||||
| @@ -157,18 +52,3 @@ type AppContext struct { | |||||||
| 	Title               string   `json:"title"` | 	Title               string   `json:"title"` | ||||||
| 	GenericName         string   `json:"genericName"` | 	GenericName         string   `json:"genericName"` | ||||||
| } | } | ||||||
|  |  | ||||||
| // Totp request is the request for the totp endpoint |  | ||||||
| type TotpRequest struct { |  | ||||||
| 	Code string `json:"code"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // Server configuration |  | ||||||
| type HandlersConfig struct { |  | ||||||
| 	AppURL          string |  | ||||||
| 	Domain          string |  | ||||||
| 	CookieSecure    bool |  | ||||||
| 	DisableContinue bool |  | ||||||
| 	GenericName     string |  | ||||||
| 	Title           string |  | ||||||
| } |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user