package middleware import ( "crypto/sha256" "crypto/subtle" "errors" "example.com/userService/internal/apierrors" "git.yuntuo.hk/k2safe/go-omni/httpx" "os" "strings" ) // Auth is the fail-closed registry default. Startup replaces it with configured // authentication. This example uses an administrator key and an optional read key; // production applications can inject JWT/OIDC through the same middleware slot. func Auth(next httpx.Handler) httpx.Handler { return func(c *httpx.Context) error { return apierrors.Unauthenticated() } } func NewAuthFromEnv() (httpx.Middleware, error) { token := os.Getenv("USER_SERVICE_API_TOKEN") if len(token) < 32 || strings.ContainsAny(token, " \t\r\n") { return nil, errors.New("USER_SERVICE_API_TOKEN requires at least 32 bytes without whitespace") } readToken := os.Getenv("USER_SERVICE_READ_TOKEN") if readToken != "" && (len(readToken) < 32 || strings.ContainsAny(readToken, " \t\r\n") || readToken == token) { return nil, errors.New("USER_SERVICE_READ_TOKEN must be a distinct token of at least 32 bytes without whitespace") } expectedRead := sha256.Sum256([]byte(readToken)) expected := sha256.Sum256([]byte(token)) return func(next httpx.Handler) httpx.Handler { return func(c *httpx.Context) error { parts := strings.Fields(c.Header("Authorization")) if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { return apierrors.Unauthenticated() } actual := sha256.Sum256([]byte(parts[1])) admin := subtle.ConstantTimeCompare(expected[:], actual[:]) == 1 read := subtle.ConstantTimeCompare(expectedRead[:], actual[:]) == 1 && readToken != "" if !admin && !read { return apierrors.Unauthenticated() } if !admin && c.Request().Method != "GET" && c.Request().Method != "HEAD" { return apierrors.Forbidden() } return next(c) } }, nil }