From b0d819335248b66bdada2639155188f00b2167c0 Mon Sep 17 00:00:00 2001 From: CodeShell <122738806+CodeShellDev@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:34:53 +0100 Subject: [PATCH 1/2] added auth method disabling/enabling --- internals/config/structure/structure.go | 11 ++ internals/config/tokens.go | 15 +- internals/proxy/middlewares/auth.go | 185 +++++++++++++++--------- 3 files changed, 143 insertions(+), 68 deletions(-) diff --git a/internals/config/structure/structure.go b/internals/config/structure/structure.go index 2c2c127e..d770f6ff 100644 --- a/internals/config/structure/structure.go +++ b/internals/config/structure/structure.go @@ -26,6 +26,17 @@ type SERVICE struct { type API struct { URL string `koanf:"url" env>aliases:".apiurl"` TOKENS []string `koanf:"tokens" env>aliases:".apitokens,.apitoken" aliases:"token"` + AUTH AUTH `koanf:"auth"` +} + +type AUTH struct { + METHODS []string `koanf:"methods" env>aliases:".authmethods"` + TOKENS []Token `koanf:"tokens" aliases:"token"` +} + +type Token struct { + Set []string `koanf:"set"` + Methods []string `koanf:"methods"` } type SETTINGS struct { diff --git a/internals/config/tokens.go b/internals/config/tokens.go index 6fb44532..f51e6045 100644 --- a/internals/config/tokens.go +++ b/internals/config/tokens.go @@ -41,7 +41,7 @@ func NormalizeTokens() { } func InitTokens() { - apiTokens := DEFAULT.API.TOKENS + apiTokens := parseAuthTokens(*DEFAULT) for _, token := range apiTokens { ENV.CONFIGS[token] = DEFAULT @@ -80,7 +80,8 @@ func parseTokenConfigs(configArray []structure.CONFIG) map[string]structure.CONF configs := map[string]structure.CONFIG{} for _, config := range configArray { - for _, token := range config.API.TOKENS { + tokens := parseAuthTokens(config) + for _, token := range tokens { configs[token] = config } } @@ -88,6 +89,16 @@ func parseTokenConfigs(configArray []structure.CONFIG) map[string]structure.CONF return configs } +func parseAuthTokens(config structure.CONFIG) []string { + tokens := config.API.TOKENS + + for _, token := range config.API.AUTH.TOKENS { + tokens = append(tokens, token.Set...) + } + + return tokens +} + func getSchemeTagByPointer(config any, tag string, fieldPointer any) string { v := reflect.ValueOf(config) if v.Kind() == reflect.Pointer { diff --git a/internals/proxy/middlewares/auth.go b/internals/proxy/middlewares/auth.go index 398d2933..3078d3da 100644 --- a/internals/proxy/middlewares/auth.go +++ b/internals/proxy/middlewares/auth.go @@ -12,6 +12,7 @@ import ( "github.com/codeshelldev/gotl/pkg/logger" "github.com/codeshelldev/gotl/pkg/request" "github.com/codeshelldev/secured-signal-api/internals/config" + "github.com/codeshelldev/secured-signal-api/internals/config/structure" ) var Auth Middleware = Middleware{ @@ -22,59 +23,6 @@ var Auth Middleware = Middleware{ const tokenKey contextKey = "token" const isAuthKey contextKey = "isAuthenticated" -func authHandler(next http.Handler) http.Handler { - var authChain = NewAuthChain(). - Use(BearerAuth). - Use(BasicAuth). - Use(BodyAuth). - Use(QueryAuth). - Use(PathAuth) - - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - tokenKeys := maps.Keys(config.ENV.CONFIGS) - tokens := slices.Collect(tokenKeys) - - if tokens == nil { - tokens = []string{} - } - - if config.ENV.INSECURE || len(tokens) <= 0 { - next.ServeHTTP(w, req) - return - } - - token, _ := authChain.Eval(w, req, tokens) - - if token == "" { - onUnauthorized(w) - - req = setContext(req, isAuthKey, false) - } else { - req = setContext(req, isAuthKey, true) - req = setContext(req, tokenKey, token) - } - - next.ServeHTTP(w, req) - }) -} - -var InternalAuthRequirement Middleware = Middleware{ - Name: "_Auth_Requirement", - Use: authRequirementHandler, -} - -func authRequirementHandler(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - isAuthenticated := getContext[bool](req, isAuthKey) - - if !isAuthenticated { - return - } - - next.ServeHTTP(w, req) - }) -} - type AuthMethod struct { Name string Authenticate func(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) @@ -240,16 +188,6 @@ var PathAuth = AuthMethod{ }, } -func onUnauthorized(w http.ResponseWriter) { - w.Header().Set("WWW-Authenticate", "Basic realm=\"Login Required\", Bearer realm=\"Access Token Required\"") - - http.Error(w, "Unauthorized", http.StatusUnauthorized) -} - -func isValidToken(tokens []string, match string) bool { - return slices.Contains(tokens, match) -} - type AuthChain struct { methods []AuthMethod } @@ -266,7 +204,7 @@ func (chain *AuthChain) Use(method AuthMethod) *AuthChain { return chain } -func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens []string) (string, error) { +func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens []string) (AuthMethod, string, error) { var err error var token string @@ -278,11 +216,126 @@ func (chain *AuthChain) Eval(w http.ResponseWriter, req *http.Request, tokens [] } if token != "" { - return token, nil + + + return method, token, nil } } logger.Warn("Client failed to provide any auth") - return "", err + return AuthMethod{}, "", err +} + +func authHandler(next http.Handler) http.Handler { + var authChain = NewAuthChain(). + Use(BearerAuth). + Use(BasicAuth). + Use(BodyAuth). + Use(QueryAuth). + Use(PathAuth) + + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + tokenKeys := maps.Keys(config.ENV.CONFIGS) + tokens := slices.Collect(tokenKeys) + + if tokens == nil { + tokens = []string{} + } + + if config.ENV.INSECURE || len(tokens) <= 0 { + next.ServeHTTP(w, req) + return + } + + method, token, _ := authChain.Eval(w, req, tokens) + + if token == "" { + onUnauthorized(w) + + req = setContext(req, isAuthKey, false) + } else { + conf := getConfigWithoutDefault(token) + + allowedMethods := conf.API.AUTH.METHODS + + if allowedMethods == nil { + allowedMethods = getConfig("").API.AUTH.METHODS + } + + if isAuthMethodAllowed(method, token, conf.API.TOKENS, conf.API.AUTH.METHODS, conf.API.AUTH.TOKENS) { + req = setContext(req, isAuthKey, true) + req = setContext(req, tokenKey, token) + } else { + logger.Warn("Client tried using disabled auth method: ", method.Name) + + onUnauthorized(w) + + req = setContext(req, isAuthKey, false) + } + } + + next.ServeHTTP(w, req) + }) +} + +var InternalAuthRequirement Middleware = Middleware{ + Name: "_Auth_Requirement", + Use: authRequirementHandler, +} + +func authRequirementHandler(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + isAuthenticated := getContext[bool](req, isAuthKey) + + if !isAuthenticated { + return + } + + next.ServeHTTP(w, req) + }) +} + +func onUnauthorized(w http.ResponseWriter) { + w.Header().Set("WWW-Authenticate", "Basic realm=\"Login Required\", Bearer realm=\"Access Token Required\"") + + http.Error(w, "Unauthorized", http.StatusUnauthorized) +} + +func isValidToken(tokens []string, match string) bool { + return slices.Contains(tokens, match) +} + +type AuthToken struct { + Token string + Methods []string +} + +func getTokenMethodMap(rawTokens []string, defaultMethods []string, tokenMethodSet []structure.Token) map[string][]string { + tokenMethodMap := map[string][]string{} + + for _, token := range rawTokens { + tokenMethodMap[token] = defaultMethods + } + + for _, set := range tokenMethodSet { + for _, token := range set.Set { + tokenMethodMap[token] = set.Methods + } + } + + return tokenMethodMap +} + +func isAuthMethodAllowed(method AuthMethod, token string, rawTokens []string, defaultMethods []string, tokenMethodSet []structure.Token) bool { + if (len(defaultMethods) == 0 || defaultMethods == nil) && (len(tokenMethodSet) == 0 || tokenMethodSet == nil) { + // default: allow all + return true + } + + tokenMethodMap := getTokenMethodMap(rawTokens, defaultMethods, tokenMethodSet) + + return slices.ContainsFunc(tokenMethodMap[token], func(try string) bool { + return strings.EqualFold(try, method.Name) + }) } \ No newline at end of file From 0cf74d9b65ed96912c6c733293f57d9f7060034a Mon Sep 17 00:00:00 2001 From: CodeShell <122738806+CodeShellDev@users.noreply.github.com> Date: Fri, 23 Jan 2026 21:35:16 +0100 Subject: [PATCH 2/2] added bearer, basic and body auth to defaults --- data/defaults.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/data/defaults.yml b/data/defaults.yml index 5703afab..2848aef0 100644 --- a/data/defaults.yml +++ b/data/defaults.yml @@ -1,7 +1,10 @@ +api: + auth: + methods: [bearer, basic, body] + service: port: 8880 - -logLevel: info + logLevel: info settings: message: