mirror of
https://github.com/labstack/echo.git
synced 2025-07-13 01:30:31 +02:00
12
bind.go
12
bind.go
@ -38,15 +38,15 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
|||||||
}
|
}
|
||||||
ctype := req.Header.Get(HeaderContentType)
|
ctype := req.Header.Get(HeaderContentType)
|
||||||
if req.ContentLength == 0 {
|
if req.ContentLength == 0 {
|
||||||
return NewHTTPError(http.StatusBadRequest, "request body can't be empty")
|
return NewHTTPError(http.StatusBadRequest, "Request body can't be empty")
|
||||||
}
|
}
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
case strings.HasPrefix(ctype, MIMEApplicationJSON):
|
||||||
if err = json.NewDecoder(req.Body).Decode(i); err != nil {
|
if err = json.NewDecoder(req.Body).Decode(i); err != nil {
|
||||||
if ute, ok := err.(*json.UnmarshalTypeError); ok {
|
if ute, ok := err.(*json.UnmarshalTypeError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unmarshal type error: expected=%v, got=%v, offset=%v", ute.Type, ute.Value, ute.Offset))
|
||||||
} else if se, ok := err.(*json.SyntaxError); ok {
|
} else if se, ok := err.(*json.SyntaxError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("syntax error: offset=%v, error=%v", se.Offset, se.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: offset=%v, error=%v", se.Offset, se.Error()))
|
||||||
} else {
|
} else {
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error())
|
return NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@ -54,9 +54,9 @@ func (b *DefaultBinder) Bind(i interface{}, c Context) (err error) {
|
|||||||
case strings.HasPrefix(ctype, MIMEApplicationXML):
|
case strings.HasPrefix(ctype, MIMEApplicationXML):
|
||||||
if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
|
if err = xml.NewDecoder(req.Body).Decode(i); err != nil {
|
||||||
if ute, ok := err.(*xml.UnsupportedTypeError); ok {
|
if ute, ok := err.(*xml.UnsupportedTypeError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Unsupported type error: type=%v, error=%v", ute.Type, ute.Error()))
|
||||||
} else if se, ok := err.(*xml.SyntaxError); ok {
|
} else if se, ok := err.(*xml.SyntaxError); ok {
|
||||||
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("syntax error: line=%v, error=%v", se.Line, se.Error()))
|
return NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Syntax error: line=%v, error=%v", se.Line, se.Error()))
|
||||||
} else {
|
} else {
|
||||||
return NewHTTPError(http.StatusBadRequest, err.Error())
|
return NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ func (b *DefaultBinder) bindData(ptr interface{}, data map[string][]string, tag
|
|||||||
val := reflect.ValueOf(ptr).Elem()
|
val := reflect.ValueOf(ptr).Elem()
|
||||||
|
|
||||||
if typ.Kind() != reflect.Struct {
|
if typ.Kind() != reflect.Struct {
|
||||||
return errors.New("binding element must be a struct")
|
return errors.New("Binding element must be a struct")
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < typ.NumField(); i++ {
|
for i := 0; i < typ.NumField(); i++ {
|
||||||
|
8
echo.go
8
echo.go
@ -221,10 +221,10 @@ var (
|
|||||||
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized)
|
ErrUnauthorized = NewHTTPError(http.StatusUnauthorized)
|
||||||
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
|
ErrMethodNotAllowed = NewHTTPError(http.StatusMethodNotAllowed)
|
||||||
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
|
ErrStatusRequestEntityTooLarge = NewHTTPError(http.StatusRequestEntityTooLarge)
|
||||||
ErrValidatorNotRegistered = errors.New("validator not registered")
|
ErrValidatorNotRegistered = errors.New("Validator not registered")
|
||||||
ErrRendererNotRegistered = errors.New("renderer not registered")
|
ErrRendererNotRegistered = errors.New("Renderer not registered")
|
||||||
ErrInvalidRedirectCode = errors.New("invalid redirect status code")
|
ErrInvalidRedirectCode = errors.New("Invalid redirect status code")
|
||||||
ErrCookieNotFound = errors.New("cookie not found")
|
ErrCookieNotFound = errors.New("Cookie not found")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Error handlers
|
// Error handlers
|
||||||
|
@ -2,6 +2,7 @@ package middleware
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
"github.com/labstack/echo"
|
"github.com/labstack/echo"
|
||||||
)
|
)
|
||||||
@ -36,7 +37,7 @@ var (
|
|||||||
//
|
//
|
||||||
// For valid credentials it calls the next handler.
|
// For valid credentials it calls the next handler.
|
||||||
// For invalid credentials, it sends "401 - Unauthorized" response.
|
// For invalid credentials, it sends "401 - Unauthorized" response.
|
||||||
// For empty or invalid `Authorization` header, it sends "400 - Bad Request" response.
|
// For missing or invalid `Authorization` header, it sends "400 - Bad Request" response.
|
||||||
func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
|
func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
|
||||||
c := DefaultBasicAuthConfig
|
c := DefaultBasicAuthConfig
|
||||||
c.Validator = fn
|
c.Validator = fn
|
||||||
@ -48,7 +49,7 @@ func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
|
|||||||
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
||||||
// Defaults
|
// Defaults
|
||||||
if config.Validator == nil {
|
if config.Validator == nil {
|
||||||
panic("basic-auth middleware requires validator function")
|
panic("basic-auth middleware requires a validator function")
|
||||||
}
|
}
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultBasicAuthConfig.Skipper
|
config.Skipper = DefaultBasicAuthConfig.Skipper
|
||||||
@ -61,6 +62,9 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
auth := c.Request().Header.Get(echo.HeaderAuthorization)
|
||||||
|
if auth == "" {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "Missing authorization header")
|
||||||
|
}
|
||||||
l := len(basic)
|
l := len(basic)
|
||||||
|
|
||||||
if len(auth) > l+1 && auth[:l] == basic {
|
if len(auth) > l+1 && auth[:l] == basic {
|
||||||
@ -77,7 +81,10 @@ func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, "Invalid authorization header")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to return `401` for browsers to pop-up login box.
|
// Need to return `401` for browsers to pop-up login box.
|
||||||
c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm=Restricted")
|
c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm=Restricted")
|
||||||
return echo.ErrUnauthorized
|
return echo.ErrUnauthorized
|
||||||
|
@ -30,21 +30,21 @@ func TestBasicAuth(t *testing.T) {
|
|||||||
req.Header.Set(echo.HeaderAuthorization, auth)
|
req.Header.Set(echo.HeaderAuthorization, auth)
|
||||||
assert.NoError(t, h(c))
|
assert.NoError(t, h(c))
|
||||||
|
|
||||||
// Incorrect password
|
// Invalid credentials
|
||||||
auth = basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:password"))
|
auth = basic + " " + base64.StdEncoding.EncodeToString([]byte("joe:invalid-password"))
|
||||||
req.Header.Set(echo.HeaderAuthorization, auth)
|
req.Header.Set(echo.HeaderAuthorization, auth)
|
||||||
he := h(c).(*echo.HTTPError)
|
he := h(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code)
|
assert.Equal(t, http.StatusUnauthorized, he.Code)
|
||||||
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.HeaderWWWAuthenticate))
|
assert.Equal(t, basic+" realm=Restricted", res.Header().Get(echo.HeaderWWWAuthenticate))
|
||||||
|
|
||||||
// Empty Authorization header
|
// Missing Authorization header
|
||||||
req.Header.Set(echo.HeaderAuthorization, "")
|
req.Header.Del(echo.HeaderAuthorization)
|
||||||
he = h(c).(*echo.HTTPError)
|
he = h(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code)
|
assert.Equal(t, http.StatusBadRequest, he.Code)
|
||||||
|
|
||||||
// Invalid Authorization header
|
// Invalid Authorization header
|
||||||
auth = base64.StdEncoding.EncodeToString([]byte("invalid"))
|
auth = base64.StdEncoding.EncodeToString([]byte("invalid"))
|
||||||
req.Header.Set(echo.HeaderAuthorization, auth)
|
req.Header.Set(echo.HeaderAuthorization, auth)
|
||||||
he = h(c).(*echo.HTTPError)
|
he = h(c).(*echo.HTTPError)
|
||||||
assert.Equal(t, http.StatusUnauthorized, he.Code)
|
assert.Equal(t, http.StatusBadRequest, he.Code)
|
||||||
}
|
}
|
||||||
|
@ -143,7 +143,7 @@ func CSRFWithConfig(config CSRFConfig) echo.MiddlewareFunc {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !validateCSRFToken(token, clientToken) {
|
if !validateCSRFToken(token, clientToken) {
|
||||||
return echo.NewHTTPError(http.StatusForbidden, "csrf token is invalid")
|
return echo.NewHTTPError(http.StatusForbidden, "CSRF token is invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ func csrfTokenFromForm(param string) csrfTokenExtractor {
|
|||||||
return func(c echo.Context) (string, error) {
|
return func(c echo.Context) (string, error) {
|
||||||
token := c.FormValue(param)
|
token := c.FormValue(param)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return "", errors.New("empty csrf token in form param")
|
return "", errors.New("Missing csrf token in form param")
|
||||||
}
|
}
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
@ -199,7 +199,7 @@ func csrfTokenFromQuery(param string) csrfTokenExtractor {
|
|||||||
return func(c echo.Context) (string, error) {
|
return func(c echo.Context) (string, error) {
|
||||||
token := c.QueryParam(param)
|
token := c.QueryParam(param)
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return "", errors.New("empty csrf token in query param")
|
return "", errors.New("Missing csrf token in query param")
|
||||||
}
|
}
|
||||||
return token, nil
|
return token, nil
|
||||||
}
|
}
|
||||||
|
@ -14,24 +14,20 @@ import (
|
|||||||
type (
|
type (
|
||||||
// JWTConfig defines the config for JWT middleware.
|
// JWTConfig defines the config for JWT middleware.
|
||||||
JWTConfig struct {
|
JWTConfig struct {
|
||||||
// AuthScheme to be used in the Authorization header.
|
|
||||||
// Optional. Default value "Bearer".
|
|
||||||
AuthScheme string
|
|
||||||
|
|
||||||
// Skipper defines a function to skip middleware.
|
// Skipper defines a function to skip middleware.
|
||||||
Skipper Skipper
|
Skipper Skipper
|
||||||
|
|
||||||
// Signing key to validate token.
|
// Signing key to validate token.
|
||||||
// Required.
|
// Required.
|
||||||
SigningKey interface{} `json:"signing_key"`
|
SigningKey interface{}
|
||||||
|
|
||||||
// Signing method, used to check token signing method.
|
// Signing method, used to check token signing method.
|
||||||
// Optional. Default value HS256.
|
// Optional. Default value HS256.
|
||||||
SigningMethod string `json:"signing_method"`
|
SigningMethod string
|
||||||
|
|
||||||
// Context key to store user information from the token into context.
|
// Context key to store user information from the token into context.
|
||||||
// Optional. Default value "user".
|
// Optional. Default value "user".
|
||||||
ContextKey string `json:"context_key"`
|
ContextKey string
|
||||||
|
|
||||||
// Claims are extendable claims data defining token content.
|
// Claims are extendable claims data defining token content.
|
||||||
// Optional. Default value jwt.MapClaims
|
// Optional. Default value jwt.MapClaims
|
||||||
@ -44,7 +40,11 @@ type (
|
|||||||
// - "header:<name>"
|
// - "header:<name>"
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "cookie:<name>"
|
// - "cookie:<name>"
|
||||||
TokenLookup string `json:"token_lookup"`
|
TokenLookup string
|
||||||
|
|
||||||
|
// AuthScheme to be used in the Authorization header.
|
||||||
|
// Optional. Default value "Bearer".
|
||||||
|
AuthScheme string
|
||||||
|
|
||||||
keyFunc jwt.Keyfunc
|
keyFunc jwt.Keyfunc
|
||||||
}
|
}
|
||||||
@ -60,11 +60,11 @@ const (
|
|||||||
var (
|
var (
|
||||||
// DefaultJWTConfig is the default JWT auth middleware config.
|
// DefaultJWTConfig is the default JWT auth middleware config.
|
||||||
DefaultJWTConfig = JWTConfig{
|
DefaultJWTConfig = JWTConfig{
|
||||||
AuthScheme: "Bearer",
|
|
||||||
Skipper: defaultSkipper,
|
Skipper: defaultSkipper,
|
||||||
SigningMethod: AlgorithmHS256,
|
SigningMethod: AlgorithmHS256,
|
||||||
ContextKey: "user",
|
ContextKey: "user",
|
||||||
TokenLookup: "header:" + echo.HeaderAuthorization,
|
TokenLookup: "header:" + echo.HeaderAuthorization,
|
||||||
|
AuthScheme: "Bearer",
|
||||||
Claims: jwt.MapClaims{},
|
Claims: jwt.MapClaims{},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -73,7 +73,7 @@ var (
|
|||||||
//
|
//
|
||||||
// For valid token, it sets the user in context and calls next handler.
|
// For valid token, it sets the user in context and calls next handler.
|
||||||
// For invalid token, it returns "401 - Unauthorized" error.
|
// For invalid token, it returns "401 - Unauthorized" error.
|
||||||
// For empty token, it returns "400 - Bad Request" error.
|
// For missing token, it returns "400 - Bad Request" error.
|
||||||
//
|
//
|
||||||
// See: https://jwt.io/introduction
|
// See: https://jwt.io/introduction
|
||||||
// See `JWTConfig.TokenLookup`
|
// See `JWTConfig.TokenLookup`
|
||||||
@ -87,9 +87,6 @@ func JWT(key []byte) echo.MiddlewareFunc {
|
|||||||
// See: `JWT()`.
|
// See: `JWT()`.
|
||||||
func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
||||||
// Defaults
|
// Defaults
|
||||||
if config.AuthScheme == "" {
|
|
||||||
config.AuthScheme = DefaultJWTConfig.AuthScheme
|
|
||||||
}
|
|
||||||
if config.Skipper == nil {
|
if config.Skipper == nil {
|
||||||
config.Skipper = DefaultJWTConfig.Skipper
|
config.Skipper = DefaultJWTConfig.Skipper
|
||||||
}
|
}
|
||||||
@ -108,6 +105,9 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
if config.TokenLookup == "" {
|
if config.TokenLookup == "" {
|
||||||
config.TokenLookup = DefaultJWTConfig.TokenLookup
|
config.TokenLookup = DefaultJWTConfig.TokenLookup
|
||||||
}
|
}
|
||||||
|
if config.AuthScheme == "" {
|
||||||
|
config.AuthScheme = DefaultJWTConfig.AuthScheme
|
||||||
|
}
|
||||||
config.keyFunc = func(t *jwt.Token) (interface{}, error) {
|
config.keyFunc = func(t *jwt.Token) (interface{}, error) {
|
||||||
// Check the signing method
|
// Check the signing method
|
||||||
if t.Method.Alg() != config.SigningMethod {
|
if t.Method.Alg() != config.SigningMethod {
|
||||||
@ -154,7 +154,7 @@ func JWTWithConfig(config JWTConfig) echo.MiddlewareFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwtFromHeader returns a `jwtExtractor` that extracts token from request header.
|
// jwtFromHeader returns a `jwtExtractor` that extracts token from the request header.
|
||||||
func jwtFromHeader(header string, authScheme string) jwtExtractor {
|
func jwtFromHeader(header string, authScheme string) jwtExtractor {
|
||||||
return func(c echo.Context) (string, error) {
|
return func(c echo.Context) (string, error) {
|
||||||
auth := c.Request().Header.Get(header)
|
auth := c.Request().Header.Get(header)
|
||||||
@ -162,28 +162,27 @@ func jwtFromHeader(header string, authScheme string) jwtExtractor {
|
|||||||
if len(auth) > l+1 && auth[:l] == authScheme {
|
if len(auth) > l+1 && auth[:l] == authScheme {
|
||||||
return auth[l+1:], nil
|
return auth[l+1:], nil
|
||||||
}
|
}
|
||||||
return "", errors.New("empty or invalid jwt in request header")
|
return "", errors.New("Missing or invalid jwt in request header")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwtFromQuery returns a `jwtExtractor` that extracts token from query string.
|
// jwtFromQuery returns a `jwtExtractor` that extracts token from the query string.
|
||||||
func jwtFromQuery(param string) jwtExtractor {
|
func jwtFromQuery(param string) jwtExtractor {
|
||||||
return func(c echo.Context) (string, error) {
|
return func(c echo.Context) (string, error) {
|
||||||
token := c.QueryParam(param)
|
token := c.QueryParam(param)
|
||||||
var err error
|
|
||||||
if token == "" {
|
if token == "" {
|
||||||
return "", errors.New("empty jwt in query string")
|
return "", errors.New("Missing jwt in query string")
|
||||||
}
|
}
|
||||||
return token, err
|
return token, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// jwtFromCookie returns a `jwtExtractor` that extracts token from named cookie.
|
// jwtFromCookie returns a `jwtExtractor` that extracts token from the named cookie.
|
||||||
func jwtFromCookie(name string) jwtExtractor {
|
func jwtFromCookie(name string) jwtExtractor {
|
||||||
return func(c echo.Context) (string, error) {
|
return func(c echo.Context) (string, error) {
|
||||||
cookie, err := c.Cookie(name)
|
cookie, err := c.Cookie(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.New("empty jwt in cookie")
|
return "", errors.New("Missing jwt in cookie")
|
||||||
}
|
}
|
||||||
return cookie.Value, nil
|
return cookie.Value, nil
|
||||||
}
|
}
|
||||||
|
131
middleware/key_auth.go
Normal file
131
middleware/key_auth.go
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// KeyAuthConfig defines the config for KeyAuth middleware.
|
||||||
|
KeyAuthConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// KeyLookup is a string in the form of "<source>:<name>" that is used
|
||||||
|
// to extract key from the request.
|
||||||
|
// Optional. Default value "header:Authorization".
|
||||||
|
// Possible values:
|
||||||
|
// - "header:<name>"
|
||||||
|
// - "query:<name>"
|
||||||
|
KeyLookup string `json:"key_lookup"`
|
||||||
|
|
||||||
|
// AuthScheme to be used in the Authorization header.
|
||||||
|
// Optional. Default value "Bearer".
|
||||||
|
AuthScheme string
|
||||||
|
|
||||||
|
// Validator is a function to validate key.
|
||||||
|
// Required.
|
||||||
|
Validator KeyAuthValidator
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyAuthValidator defines a function to validate KeyAuth credentials.
|
||||||
|
KeyAuthValidator func(string) bool
|
||||||
|
|
||||||
|
keyExtractor func(echo.Context) (string, error)
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// DefaultKeyAuthConfig is the default KeyAuth middleware config.
|
||||||
|
DefaultKeyAuthConfig = KeyAuthConfig{
|
||||||
|
Skipper: defaultSkipper,
|
||||||
|
KeyLookup: "header:" + echo.HeaderAuthorization,
|
||||||
|
AuthScheme: "Bearer",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeyAuth returns an KeyAuth middleware.
|
||||||
|
//
|
||||||
|
// For valid key it calls the next handler.
|
||||||
|
// For invalid key, it sends "401 - Unauthorized" response.
|
||||||
|
// For missing key, it sends "400 - Bad Request" response.
|
||||||
|
func KeyAuth(fn KeyAuthValidator) echo.MiddlewareFunc {
|
||||||
|
c := DefaultKeyAuthConfig
|
||||||
|
c.Validator = fn
|
||||||
|
return KeyAuthWithConfig(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// KeyAuthWithConfig returns an KeyAuth middleware with config.
|
||||||
|
// See `KeyAuth()`.
|
||||||
|
func KeyAuthWithConfig(config KeyAuthConfig) echo.MiddlewareFunc {
|
||||||
|
// Defaults
|
||||||
|
if config.Skipper == nil {
|
||||||
|
config.Skipper = DefaultKeyAuthConfig.Skipper
|
||||||
|
}
|
||||||
|
// Defaults
|
||||||
|
if config.AuthScheme == "" {
|
||||||
|
config.AuthScheme = DefaultKeyAuthConfig.AuthScheme
|
||||||
|
}
|
||||||
|
if config.KeyLookup == "" {
|
||||||
|
config.KeyLookup = DefaultKeyAuthConfig.KeyLookup
|
||||||
|
}
|
||||||
|
if config.Validator == nil {
|
||||||
|
panic("key-auth middleware requires a validator function")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
parts := strings.Split(config.KeyLookup, ":")
|
||||||
|
extractor := keyFromHeader(parts[1], config.AuthScheme)
|
||||||
|
switch parts[0] {
|
||||||
|
case "query":
|
||||||
|
extractor = keyFromQuery(parts[1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return func(next echo.HandlerFunc) echo.HandlerFunc {
|
||||||
|
return func(c echo.Context) error {
|
||||||
|
if config.Skipper(c) {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract and verify key
|
||||||
|
key, err := extractor(c)
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
if config.Validator(key) {
|
||||||
|
return next(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return echo.ErrUnauthorized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyFromHeader returns a `keyExtractor` that extracts key from the request header.
|
||||||
|
func keyFromHeader(header string, authScheme string) keyExtractor {
|
||||||
|
return func(c echo.Context) (string, error) {
|
||||||
|
auth := c.Request().Header.Get(header)
|
||||||
|
if header == echo.HeaderAuthorization {
|
||||||
|
l := len(authScheme)
|
||||||
|
if len(auth) > l+1 && auth[:l] == authScheme {
|
||||||
|
return auth[l+1:], nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return auth, nil
|
||||||
|
}
|
||||||
|
return "", errors.New("Missing or invalid key in request header")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyFromQuery returns a `keyExtractor` that extracts key from the query string.
|
||||||
|
func keyFromQuery(param string) keyExtractor {
|
||||||
|
return func(c echo.Context) (string, error) {
|
||||||
|
key := c.QueryParam(param)
|
||||||
|
if key == "" {
|
||||||
|
return "", errors.New("Missing key in query string")
|
||||||
|
}
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
}
|
59
middleware/key_auth_test.go
Normal file
59
middleware/key_auth_test.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/labstack/echo"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestKeyAuth(t *testing.T) {
|
||||||
|
e := echo.New()
|
||||||
|
req, _ := http.NewRequest(echo.GET, "/", nil)
|
||||||
|
res := httptest.NewRecorder()
|
||||||
|
c := e.NewContext(req, res)
|
||||||
|
config := KeyAuthConfig{
|
||||||
|
Validator: func(key string) bool {
|
||||||
|
return key == "valid-key"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
h := KeyAuthWithConfig(config)(func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Valid key
|
||||||
|
auth := DefaultKeyAuthConfig.AuthScheme + " " + "valid-key"
|
||||||
|
req.Header.Set(echo.HeaderAuthorization, auth)
|
||||||
|
assert.NoError(t, h(c))
|
||||||
|
|
||||||
|
// Invalid key
|
||||||
|
auth = DefaultKeyAuthConfig.AuthScheme + " " + "invalid-key"
|
||||||
|
req.Header.Set(echo.HeaderAuthorization, auth)
|
||||||
|
he := h(c).(*echo.HTTPError)
|
||||||
|
assert.Equal(t, http.StatusUnauthorized, he.Code)
|
||||||
|
|
||||||
|
// Missing Authorization header
|
||||||
|
req.Header.Del(echo.HeaderAuthorization)
|
||||||
|
he = h(c).(*echo.HTTPError)
|
||||||
|
assert.Equal(t, http.StatusBadRequest, he.Code)
|
||||||
|
|
||||||
|
// Key from custom header
|
||||||
|
config.KeyLookup = "header:API-Key"
|
||||||
|
h = KeyAuthWithConfig(config)(func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
})
|
||||||
|
req.Header.Set("API-Key", "valid-key")
|
||||||
|
assert.NoError(t, h(c))
|
||||||
|
|
||||||
|
// Key from query string
|
||||||
|
config.KeyLookup = "query:key"
|
||||||
|
h = KeyAuthWithConfig(config)(func(c echo.Context) error {
|
||||||
|
return c.String(http.StatusOK, "test")
|
||||||
|
})
|
||||||
|
q := req.URL.Query()
|
||||||
|
q.Add("key", "valid-key")
|
||||||
|
req.URL.RawQuery = q.Encode()
|
||||||
|
assert.NoError(t, h(c))
|
||||||
|
}
|
@ -11,12 +11,11 @@ Basic auth middleware provides an HTTP basic authentication.
|
|||||||
|
|
||||||
- For valid credentials it calls the next handler.
|
- For valid credentials it calls the next handler.
|
||||||
- For invalid credentials, it sends "401 - Unauthorized" response.
|
- For invalid credentials, it sends "401 - Unauthorized" response.
|
||||||
- For empty or invalid `Authorization` header, it sends "400 - Bad Request" response.
|
- For missing or invalid `Authorization` header, it sends "400 - Bad Request" response.
|
||||||
|
|
||||||
*Usage*
|
*Usage*
|
||||||
|
|
||||||
```go
|
```go
|
||||||
e := echo.New()
|
|
||||||
e.Use(middleware.BasicAuth(func(username, password string) bool {
|
e.Use(middleware.BasicAuth(func(username, password string) bool {
|
||||||
if username == "joe" && password == "secret" {
|
if username == "joe" && password == "secret" {
|
||||||
return true
|
return true
|
||||||
@ -30,9 +29,7 @@ e.Use(middleware.BasicAuth(func(username, password string) bool {
|
|||||||
*Usage*
|
*Usage*
|
||||||
|
|
||||||
```go
|
```go
|
||||||
e := echo.New()
|
e.Use(middleware.BasicAuthWithConfig(middleware.BasicAuthConfig{}}))
|
||||||
e.Use(middleware.BasicAuthWithConfig(middleware.BasicAuthConfig{},
|
|
||||||
}))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
@ -11,7 +11,7 @@ JWT provides a JSON Web Token (JWT) authentication middleware.
|
|||||||
|
|
||||||
- For valid token, it sets the user in context and calls next handler.
|
- For valid token, it sets the user in context and calls next handler.
|
||||||
- For invalid token, it sends "401 - Unauthorized" response.
|
- For invalid token, it sends "401 - Unauthorized" response.
|
||||||
- For empty or invalid `Authorization` header, it sends "400 - Bad Request".
|
- For missing or invalid `Authorization` header, it sends "400 - Bad Request".
|
||||||
|
|
||||||
*Usage*
|
*Usage*
|
||||||
|
|
||||||
@ -22,7 +22,6 @@ JWT provides a JSON Web Token (JWT) authentication middleware.
|
|||||||
*Usage*
|
*Usage*
|
||||||
|
|
||||||
```go
|
```go
|
||||||
e := echo.New()
|
|
||||||
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
|
e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
|
||||||
SigningKey: []byte("secret"),
|
SigningKey: []byte("secret"),
|
||||||
TokenLookup: "query:token",
|
TokenLookup: "query:token",
|
||||||
@ -34,24 +33,20 @@ e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
|
|||||||
```go
|
```go
|
||||||
// JWTConfig defines the config for JWT middleware.
|
// JWTConfig defines the config for JWT middleware.
|
||||||
JWTConfig struct {
|
JWTConfig struct {
|
||||||
// AuthScheme to be used in the Authorization header.
|
|
||||||
// Optional. Default value "Bearer".
|
|
||||||
AuthScheme string
|
|
||||||
|
|
||||||
// Skipper defines a function to skip middleware.
|
// Skipper defines a function to skip middleware.
|
||||||
Skipper Skipper
|
Skipper Skipper
|
||||||
|
|
||||||
// Signing key to validate token.
|
// Signing key to validate token.
|
||||||
// Required.
|
// Required.
|
||||||
SigningKey interface{} `json:"signing_key"`
|
SigningKey interface{}
|
||||||
|
|
||||||
// Signing method, used to check token signing method.
|
// Signing method, used to check token signing method.
|
||||||
// Optional. Default value HS256.
|
// Optional. Default value HS256.
|
||||||
SigningMethod string `json:"signing_method"`
|
SigningMethod string
|
||||||
|
|
||||||
// Context key to store user information from the token into context.
|
// Context key to store user information from the token into context.
|
||||||
// Optional. Default value "user".
|
// Optional. Default value "user".
|
||||||
ContextKey string `json:"context_key"`
|
ContextKey string
|
||||||
|
|
||||||
// Claims are extendable claims data defining token content.
|
// Claims are extendable claims data defining token content.
|
||||||
// Optional. Default value jwt.MapClaims
|
// Optional. Default value jwt.MapClaims
|
||||||
@ -64,7 +59,11 @@ JWTConfig struct {
|
|||||||
// - "header:<name>"
|
// - "header:<name>"
|
||||||
// - "query:<name>"
|
// - "query:<name>"
|
||||||
// - "cookie:<name>"
|
// - "cookie:<name>"
|
||||||
TokenLookup string `json:"token_lookup"`
|
TokenLookup string
|
||||||
|
|
||||||
|
// AuthScheme to be used in the Authorization header.
|
||||||
|
// Optional. Default value "Bearer".
|
||||||
|
AuthScheme string
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -76,6 +75,7 @@ DefaultJWTConfig = JWTConfig{
|
|||||||
SigningMethod: AlgorithmHS256,
|
SigningMethod: AlgorithmHS256,
|
||||||
ContextKey: "user",
|
ContextKey: "user",
|
||||||
TokenLookup: "header:" + echo.HeaderAuthorization,
|
TokenLookup: "header:" + echo.HeaderAuthorization,
|
||||||
|
AuthScheme: "Bearer",
|
||||||
Claims: jwt.MapClaims{},
|
Claims: jwt.MapClaims{},
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
69
website/content/middleware/key-auth.md
Normal file
69
website/content/middleware/key-auth.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
+++
|
||||||
|
title = "Key Auth Middleware"
|
||||||
|
description = "Key auth middleware for Echo"
|
||||||
|
[menu.main]
|
||||||
|
name = "Key Auth"
|
||||||
|
parent = "middleware"
|
||||||
|
weight = 5
|
||||||
|
+++
|
||||||
|
|
||||||
|
Key auth middleware provides a key based authentication.
|
||||||
|
|
||||||
|
- For valid key it calls the next handler.
|
||||||
|
- For invalid key, it sends "401 - Unauthorized" response.
|
||||||
|
- For missing key, it sends "400 - Bad Request" response.
|
||||||
|
|
||||||
|
*Usage*
|
||||||
|
|
||||||
|
```go
|
||||||
|
e.Use(middleware.KeyAuth(func(key string) bool {
|
||||||
|
return key == "valid-key"
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Custom Configuration
|
||||||
|
|
||||||
|
*Usage*
|
||||||
|
|
||||||
|
```go
|
||||||
|
e := echo.New()
|
||||||
|
e.Use(middleware.KeyAuthWithConfig(middleware.KeyAuthConfig{
|
||||||
|
KeyLookup: "query:api-key",
|
||||||
|
}))
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```go
|
||||||
|
// KeyAuthConfig defines the config for KeyAuth middleware.
|
||||||
|
KeyAuthConfig struct {
|
||||||
|
// Skipper defines a function to skip middleware.
|
||||||
|
Skipper Skipper
|
||||||
|
|
||||||
|
// KeyLookup is a string in the form of "<source>:<name>" that is used
|
||||||
|
// to extract key from the request.
|
||||||
|
// Optional. Default value "header:Authorization".
|
||||||
|
// Possible values:
|
||||||
|
// - "header:<name>"
|
||||||
|
// - "query:<name>"
|
||||||
|
KeyLookup string `json:"key_lookup"`
|
||||||
|
|
||||||
|
// AuthScheme to be used in the Authorization header.
|
||||||
|
// Optional. Default value "Bearer".
|
||||||
|
AuthScheme string
|
||||||
|
|
||||||
|
// Validator is a function to validate key.
|
||||||
|
// Required.
|
||||||
|
Validator KeyAuthValidator
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
*Default Configuration*
|
||||||
|
|
||||||
|
```go
|
||||||
|
DefaultKeyAuthConfig = KeyAuthConfig{
|
||||||
|
Skipper: defaultSkipper,
|
||||||
|
KeyLookup: "header:" + echo.HeaderAuthorization,
|
||||||
|
AuthScheme: "Bearer",
|
||||||
|
}
|
||||||
|
```
|
Reference in New Issue
Block a user