feat: declare API errors and authentication with developer tooling
This commit is contained in:
@@ -4,18 +4,18 @@ import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
apierrors "git.yuntuo.hk/k2safe/go-omni/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 administrative example uses one environment-only key;
|
||||
// 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.Unauthorized("UNAUTHENTICATED", "authentication required")
|
||||
return apierrors.Unauthenticated()
|
||||
}
|
||||
}
|
||||
func NewAuthFromEnv() (httpx.Middleware, error) {
|
||||
@@ -23,16 +23,26 @@ func NewAuthFromEnv() (httpx.Middleware, error) {
|
||||
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.Unauthorized("UNAUTHENTICATED", "authentication required")
|
||||
return apierrors.Unauthenticated()
|
||||
}
|
||||
actual := sha256.Sum256([]byte(parts[1]))
|
||||
if subtle.ConstantTimeCompare(expected[:], actual[:]) != 1 {
|
||||
return apierrors.Unauthorized("UNAUTHENTICATED", "authentication required")
|
||||
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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user