mirror of
				https://github.com/steveiliop56/tinyauth.git
				synced 2025-11-04 08:05:42 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			91048c16f8
			...
			feat/multi
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					df10eb65ce | ||
| 
						 | 
					8bf5a6067e | 
							
								
								
									
										17
									
								
								cmd/root.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								cmd/root.go
									
									
									
									
									
								
							@@ -105,12 +105,8 @@ var rootCmd = &cobra.Command{
 | 
			
		||||
 | 
			
		||||
		// Create api config
 | 
			
		||||
		apiConfig := types.APIConfig{
 | 
			
		||||
			Port:          config.Port,
 | 
			
		||||
			Address:       config.Address,
 | 
			
		||||
			Secret:        config.Secret,
 | 
			
		||||
			CookieSecure:  config.CookieSecure,
 | 
			
		||||
			SessionExpiry: config.SessionExpiry,
 | 
			
		||||
			Domain:        domain,
 | 
			
		||||
			Port:    config.Port,
 | 
			
		||||
			Address: config.Address,
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Create docker service
 | 
			
		||||
@@ -121,7 +117,14 @@ var rootCmd = &cobra.Command{
 | 
			
		||||
		HandleError(err, "Failed to initialize docker")
 | 
			
		||||
 | 
			
		||||
		// 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
 | 
			
		||||
		providers := providers.NewProviders(oauthConfig)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							@@ -3,7 +3,6 @@ module tinyauth
 | 
			
		||||
go 1.23.2
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/gin-contrib/sessions v1.0.2
 | 
			
		||||
	github.com/gin-gonic/gin v1.10.0
 | 
			
		||||
	github.com/go-playground/validator/v10 v10.24.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/goccy/go-json v0.10.4 // 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/sessions v1.2.2 // indirect
 | 
			
		||||
	github.com/gorilla/sessions v1.2.2
 | 
			
		||||
	github.com/hashicorp/hcl v1.0.0 // indirect
 | 
			
		||||
	github.com/inconshreveable/mousetrap v1.1.0 // 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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
 | 
			
		||||
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/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
 | 
			
		||||
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/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 | 
			
		||||
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/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
 | 
			
		||||
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
 | 
			
		||||
 
 | 
			
		||||
@@ -11,23 +11,21 @@ import (
 | 
			
		||||
	"tinyauth/internal/handlers"
 | 
			
		||||
	"tinyauth/internal/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-contrib/sessions"
 | 
			
		||||
	"github.com/gin-contrib/sessions/cookie"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func NewAPI(config types.APIConfig, handlers *handlers.Handlers) *API {
 | 
			
		||||
	return &API{
 | 
			
		||||
		Config:   config,
 | 
			
		||||
		Handlers: handlers,
 | 
			
		||||
		Config:   config,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type API struct {
 | 
			
		||||
	Config   types.APIConfig
 | 
			
		||||
	Router   *gin.Engine
 | 
			
		||||
	Handlers *handlers.Handlers
 | 
			
		||||
	Config   types.APIConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (api *API) Init() {
 | 
			
		||||
@@ -51,21 +49,6 @@ func (api *API) Init() {
 | 
			
		||||
	log.Debug().Msg("Setting up file server")
 | 
			
		||||
	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
 | 
			
		||||
	router.Use(func(c *gin.Context) {
 | 
			
		||||
		// If not an API request, serve the UI
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,16 @@ import (
 | 
			
		||||
	"github.com/magiconair/properties/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// User
 | 
			
		||||
var User = types.User{
 | 
			
		||||
	Username: "user",
 | 
			
		||||
	Password: "$2a$10$AvGHLTYv3xiRJ0xV9xs3XeVIlkGTygI9nqIamFYB5Xu.5.0UWF7B6", // pass
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Simple API config for tests
 | 
			
		||||
var apiConfig = types.APIConfig{
 | 
			
		||||
	Port:          8080,
 | 
			
		||||
	Address:       "0.0.0.0",
 | 
			
		||||
	Secret:        "super-secret-api-thing-for-tests", // It is 32 chars long
 | 
			
		||||
	CookieSecure:  false,
 | 
			
		||||
	SessionExpiry: 3600,
 | 
			
		||||
	Port:    8080,
 | 
			
		||||
	Address: "0.0.0.0",
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Simple handlers config for tests
 | 
			
		||||
@@ -38,15 +41,21 @@ var handlersConfig = types.HandlersConfig{
 | 
			
		||||
	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
 | 
			
		||||
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
 | 
			
		||||
func getAPI(t *testing.T) *api.API {
 | 
			
		||||
	// Create docker service
 | 
			
		||||
@@ -61,12 +70,7 @@ func getAPI(t *testing.T) *api.API {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Create auth service
 | 
			
		||||
	auth := auth.NewAuth(docker, types.Users{
 | 
			
		||||
		{
 | 
			
		||||
			Username: user.Username,
 | 
			
		||||
			Password: user.Password,
 | 
			
		||||
		},
 | 
			
		||||
	}, nil, apiConfig.SessionExpiry)
 | 
			
		||||
	auth := auth.NewAuth(authConfig, docker)
 | 
			
		||||
 | 
			
		||||
	// Create providers service
 | 
			
		||||
	providers := providers.NewProviders(types.OAuthConfig{})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
package auth
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"slices"
 | 
			
		||||
	"strings"
 | 
			
		||||
@@ -8,31 +9,27 @@ import (
 | 
			
		||||
	"tinyauth/internal/docker"
 | 
			
		||||
	"tinyauth/internal/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/gin-contrib/sessions"
 | 
			
		||||
	"github.com/gin-gonic/gin"
 | 
			
		||||
	"github.com/gorilla/sessions"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
	"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{
 | 
			
		||||
		Docker:         docker,
 | 
			
		||||
		Users:          userList,
 | 
			
		||||
		OAuthWhitelist: oauthWhitelist,
 | 
			
		||||
		SessionExpiry:  sessionExpiry,
 | 
			
		||||
		Docker: docker,
 | 
			
		||||
		Config: config,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Auth struct {
 | 
			
		||||
	Users          types.Users
 | 
			
		||||
	Docker         *docker.Docker
 | 
			
		||||
	OAuthWhitelist []string
 | 
			
		||||
	SessionExpiry  int
 | 
			
		||||
	Docker *docker.Docker
 | 
			
		||||
	Config types.AuthConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *Auth) GetUser(username string) *types.User {
 | 
			
		||||
	// 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 {
 | 
			
		||||
			return &user
 | 
			
		||||
		}
 | 
			
		||||
@@ -47,12 +44,12 @@ func (auth *Auth) CheckPassword(user types.User, password string) bool {
 | 
			
		||||
 | 
			
		||||
func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
 | 
			
		||||
	// If the whitelist is empty, allow all emails
 | 
			
		||||
	if len(auth.OAuthWhitelist) == 0 {
 | 
			
		||||
	if len(auth.Config.OAuthWhitelist) == 0 {
 | 
			
		||||
		return true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// 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 {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
@@ -62,11 +59,35 @@ func (auth *Auth) EmailWhitelisted(emailSrc string) bool {
 | 
			
		||||
	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")
 | 
			
		||||
 | 
			
		||||
	// Get cookie store
 | 
			
		||||
	store := auth.GetCookieStore()
 | 
			
		||||
 | 
			
		||||
	// Get session
 | 
			
		||||
	sessions := sessions.Default(c)
 | 
			
		||||
	sessions, err := store.Get(c.Request, "tinyauth")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	log.Debug().Msg("Setting session cookie")
 | 
			
		||||
 | 
			
		||||
@@ -76,43 +97,73 @@ func (auth *Auth) CreateSessionCookie(c *gin.Context, data *types.SessionCookie)
 | 
			
		||||
	if data.TotpPending {
 | 
			
		||||
		sessionExpiry = 3600
 | 
			
		||||
	} else {
 | 
			
		||||
		sessionExpiry = auth.SessionExpiry
 | 
			
		||||
		sessionExpiry = auth.Config.SessionExpiry
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set data
 | 
			
		||||
	sessions.Set("username", data.Username)
 | 
			
		||||
	sessions.Set("provider", data.Provider)
 | 
			
		||||
	sessions.Set("expiry", time.Now().Add(time.Duration(sessionExpiry)*time.Second).Unix())
 | 
			
		||||
	sessions.Set("totpPending", data.TotpPending)
 | 
			
		||||
	sessions.Values["username"] = data.Username
 | 
			
		||||
	sessions.Values["provider"] = data.Provider
 | 
			
		||||
	sessions.Values["expiry"] = time.Now().Add(time.Duration(sessionExpiry) * time.Second).Unix()
 | 
			
		||||
	sessions.Values["totpPending"] = data.TotpPending
 | 
			
		||||
 | 
			
		||||
	// Save session
 | 
			
		||||
	sessions.Save()
 | 
			
		||||
	err = sessions.Save(c.Request, c.Writer)
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Return nil
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *Auth) DeleteSessionCookie(c *gin.Context) {
 | 
			
		||||
func (auth *Auth) DeleteSessionCookie(c *gin.Context) error {
 | 
			
		||||
	log.Debug().Msg("Deleting session cookie")
 | 
			
		||||
 | 
			
		||||
	// Get cookie store
 | 
			
		||||
	store := auth.GetCookieStore()
 | 
			
		||||
 | 
			
		||||
	// Get session
 | 
			
		||||
	sessions := sessions.Default(c)
 | 
			
		||||
	sessions, err := store.Get(c.Request, "tinyauth")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Clear session
 | 
			
		||||
	sessions.Clear()
 | 
			
		||||
	for key := range sessions.Values {
 | 
			
		||||
		delete(sessions.Values, key)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Save session
 | 
			
		||||
	sessions.Save()
 | 
			
		||||
	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 {
 | 
			
		||||
func (auth *Auth) GetSessionCookie(c *gin.Context) (types.SessionCookie, error) {
 | 
			
		||||
	log.Debug().Msg("Getting session cookie")
 | 
			
		||||
 | 
			
		||||
	// Get cookie store
 | 
			
		||||
	store := auth.GetCookieStore()
 | 
			
		||||
 | 
			
		||||
	// Get session
 | 
			
		||||
	sessions := sessions.Default(c)
 | 
			
		||||
	sessions, err := store.Get(c.Request, "tinyauth")
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return types.SessionCookie{}, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Get data
 | 
			
		||||
	cookieUsername := sessions.Get("username")
 | 
			
		||||
	cookieProvider := sessions.Get("provider")
 | 
			
		||||
	cookieExpiry := sessions.Get("expiry")
 | 
			
		||||
	cookieTotpPending := sessions.Get("totpPending")
 | 
			
		||||
	cookieUsername := sessions.Values["username"]
 | 
			
		||||
	cookieProvider := sessions.Values["provider"]
 | 
			
		||||
	cookieExpiry := sessions.Values["expiry"]
 | 
			
		||||
	cookieTotpPending := sessions.Values["totpPending"]
 | 
			
		||||
 | 
			
		||||
	// Convert interfaces to correct types
 | 
			
		||||
	username, usernameOk := cookieUsername.(string)
 | 
			
		||||
@@ -123,7 +174,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
 | 
			
		||||
	// Check if the cookie is invalid
 | 
			
		||||
	if !usernameOk || !providerOk || !expiryOk || !totpPendingOk {
 | 
			
		||||
		log.Warn().Msg("Session cookie invalid")
 | 
			
		||||
		return types.SessionCookie{}
 | 
			
		||||
		return types.SessionCookie{}, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Check if the cookie has expired
 | 
			
		||||
@@ -134,7 +185,7 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
 | 
			
		||||
		auth.DeleteSessionCookie(c)
 | 
			
		||||
 | 
			
		||||
		// 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")
 | 
			
		||||
@@ -144,12 +195,12 @@ func (auth *Auth) GetSessionCookie(c *gin.Context) types.SessionCookie {
 | 
			
		||||
		Username:    username,
 | 
			
		||||
		Provider:    provider,
 | 
			
		||||
		TotpPending: totpPending,
 | 
			
		||||
	}
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (auth *Auth) UserAuthConfigured() bool {
 | 
			
		||||
	// 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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -19,20 +19,20 @@ import (
 | 
			
		||||
 | 
			
		||||
func NewHandlers(config types.HandlersConfig, auth *auth.Auth, hooks *hooks.Hooks, providers *providers.Providers, docker *docker.Docker) *Handlers {
 | 
			
		||||
	return &Handlers{
 | 
			
		||||
		Config:    config,
 | 
			
		||||
		Auth:      auth,
 | 
			
		||||
		Hooks:     hooks,
 | 
			
		||||
		Providers: providers,
 | 
			
		||||
		Docker:    docker,
 | 
			
		||||
		Config:    config,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Handlers struct {
 | 
			
		||||
	Config    types.HandlersConfig
 | 
			
		||||
	Auth      *auth.Auth
 | 
			
		||||
	Hooks     *hooks.Hooks
 | 
			
		||||
	Providers *providers.Providers
 | 
			
		||||
	Docker    *docker.Docker
 | 
			
		||||
	Config    types.HandlersConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (h *Handlers) AuthHandler(c *gin.Context) {
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,13 @@ type Hooks struct {
 | 
			
		||||
 | 
			
		||||
func (hooks *Hooks) UseUserContext(c *gin.Context) types.UserContext {
 | 
			
		||||
	// 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)
 | 
			
		||||
 | 
			
		||||
	// Check if basic auth is set
 | 
			
		||||
 
 | 
			
		||||
@@ -14,10 +14,10 @@ func NewOAuth(config oauth2.Config) *OAuth {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type OAuth struct {
 | 
			
		||||
	Config   oauth2.Config
 | 
			
		||||
	Verifier string
 | 
			
		||||
	Context  context.Context
 | 
			
		||||
	Token    *oauth2.Token
 | 
			
		||||
	Verifier string
 | 
			
		||||
	Config   oauth2.Config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (oauth *OAuth) Init() {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,11 +17,11 @@ func NewProviders(config types.OAuthConfig) *Providers {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Providers struct {
 | 
			
		||||
	Config    types.OAuthConfig
 | 
			
		||||
	Github    *oauth.OAuth
 | 
			
		||||
	Google    *oauth.OAuth
 | 
			
		||||
	Tailscale *oauth.OAuth
 | 
			
		||||
	Generic   *oauth.OAuth
 | 
			
		||||
	Config    types.OAuthConfig
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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"
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
type User struct {
 | 
			
		||||
	Username   string
 | 
			
		||||
@@ -23,82 +12,6 @@ type User struct {
 | 
			
		||||
// Users is a list of users
 | 
			
		||||
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
 | 
			
		||||
type OAuthProviders struct {
 | 
			
		||||
	Github    *oauth.OAuth
 | 
			
		||||
@@ -106,12 +19,6 @@ type OAuthProviders struct {
 | 
			
		||||
	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)
 | 
			
		||||
type SessionCookie struct {
 | 
			
		||||
	Username    string
 | 
			
		||||
@@ -127,25 +34,13 @@ type TinyauthLabels struct {
 | 
			
		||||
	Headers        map[string]string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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"`
 | 
			
		||||
// UserContext is the context for the user
 | 
			
		||||
type UserContext struct {
 | 
			
		||||
	Username    string
 | 
			
		||||
	IsLoggedIn  bool
 | 
			
		||||
	OAuth       bool
 | 
			
		||||
	Provider    string
 | 
			
		||||
	TotpPending bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// App Context is the response for the app context endpoint
 | 
			
		||||
@@ -157,18 +52,3 @@ type AppContext struct {
 | 
			
		||||
	Title               string   `json:"title"`
 | 
			
		||||
	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