// BasicAuth returns an HTTP basic authentication middleware. // // For valid credentials it calls the next handler. // For invalid credentials, it sends "401 - Unauthorized" response. func BasicAuth(fn BasicValidateFunc) vodka.HandlerFunc { return func(c *vodka.Context) error { // Skip WebSocket if (c.Request().Header.Get(vodka.Upgrade)) == vodka.WebSocket { return nil } auth := c.Request().Header.Get(vodka.Authorization) l := len(Basic) if len(auth) > l+1 && auth[:l] == Basic { b, err := base64.StdEncoding.DecodeString(auth[l+1:]) if err == nil { cred := string(b) for i := 0; i < len(cred); i++ { if cred[i] == ':' { // Verify credentials if fn(cred[:i], cred[i+1:]) { return nil } } } } } c.Response().Header().Set(vodka.WWWAuthenticate, Basic+" realm=Restricted") return vodka.NewHTTPError(http.StatusUnauthorized) } }
func TestGzipErrorReturned(t *testing.T) { e := vodka.New() e.Use(Gzip()) e.GET("/", func(c vodka.Context) error { return vodka.NewHTTPError(http.StatusInternalServerError, "error") }) req := test.NewRequest(vodka.GET, "/", nil) rec := test.NewResponseRecorder() e.ServeHTTP(req, rec) assert.Empty(t, rec.Header().Get(vodka.HeaderContentEncoding)) assert.Equal(t, "error", rec.Body.String()) }
// A JSON Web Token middleware func JWTAuther(opts ...Options) vodka.HandlerFunc { opt := prepareOptions(opts) return func(ctx *vodka.Context) error { if !opt.CheckWebSocket { // Skip WebSocket if (ctx.Request().Header.Get(vodka.Upgrade)) == vodka.WebSocket { return nil } } key, err := opt.KeyFunc(ctx) if err != nil { return err } auth := ctx.Request().Header.Get("Authorization") l := len(Bearer) he := vodka.NewHTTPError(http.StatusUnauthorized) if len(auth) > l+1 && auth[:l] == Bearer { t, err := jwt.Parse(auth[l+1:], func(token *jwt.Token) (interface{}, error) { // Always check the signing method if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"]) } // Return the key for validation return []byte(key), nil }) if err == nil && t.Valid { // Store token claims ctx.Set(JWTContextKey, t.Claims) return nil } } return he } }
// CSRFWithConfig returns a CSRF middleware with config. // See `CSRF()`. func CSRFWithConfig(config CSRFConfig) vodka.MiddlewareFunc { // Defaults if config.Skipper == nil { config.Skipper = DefaultCSRFConfig.Skipper } if config.TokenLength == 0 { config.TokenLength = DefaultCSRFConfig.TokenLength } if config.TokenLookup == "" { config.TokenLookup = DefaultCSRFConfig.TokenLookup } if config.ContextKey == "" { config.ContextKey = DefaultCSRFConfig.ContextKey } if config.CookieName == "" { config.CookieName = DefaultCSRFConfig.CookieName } if config.CookieMaxAge == 0 { config.CookieMaxAge = DefaultCSRFConfig.CookieMaxAge } // Initialize parts := strings.Split(config.TokenLookup, ":") extractor := csrfTokenFromHeader(parts[1]) switch parts[0] { case "form": extractor = csrfTokenFromForm(parts[1]) case "query": extractor = csrfTokenFromQuery(parts[1]) } return func(next vodka.HandlerFunc) vodka.HandlerFunc { return func(c vodka.Context) error { if config.Skipper(c) { return next(c) } req := c.Request() k, err := c.Cookie(config.CookieName) token := "" if err != nil { // Generate token token = random.String(config.TokenLength) } else { // Reuse token token = k.Value() } switch req.Method() { case vodka.GET, vodka.HEAD, vodka.OPTIONS, vodka.TRACE: default: // Validate token only for requests which are not defined as 'safe' by RFC7231 clientToken, err := extractor(c) if err != nil { return err } if !validateCSRFToken(token, clientToken) { return vodka.NewHTTPError(http.StatusForbidden, "csrf token is invalid") } } // Set CSRF cookie cookie := new(vodka.Cookie) cookie.SetName(config.CookieName) cookie.SetValue(token) if config.CookiePath != "" { cookie.SetPath(config.CookiePath) } if config.CookieDomain != "" { cookie.SetDomain(config.CookieDomain) } cookie.SetExpires(time.Now().Add(time.Duration(config.CookieMaxAge) * time.Second)) cookie.SetSecure(config.CookieSecure) cookie.SetHTTPOnly(config.CookieHTTPOnly) c.SetCookie(cookie) // Store token in the context c.Set(config.ContextKey, token) // Protect clients from caching the response c.Response().Header().Add(vodka.HeaderVary, vodka.HeaderCookie) return next(c) } } }
// JWTWithConfig returns a JWT auth middleware with config. // See: `JWT()`. func JWTWithConfig(config JWTConfig) vodka.MiddlewareFunc { // Defaults if config.Skipper == nil { config.Skipper = DefaultJWTConfig.Skipper } if config.SigningKey == nil { panic("jwt middleware requires signing key") } if config.SigningMethod == "" { config.SigningMethod = DefaultJWTConfig.SigningMethod } if config.ContextKey == "" { config.ContextKey = DefaultJWTConfig.ContextKey } if config.Claims == nil { config.Claims = DefaultJWTConfig.Claims } if config.TokenLookup == "" { config.TokenLookup = DefaultJWTConfig.TokenLookup } config.keyFunc = func(t *jwt.Token) (interface{}, error) { // Check the signing method if t.Method.Alg() != config.SigningMethod { return nil, fmt.Errorf("unexpected jwt signing method=%v", t.Header["alg"]) } return config.SigningKey, nil } // Initialize parts := strings.Split(config.TokenLookup, ":") extractor := jwtFromHeader(parts[1]) switch parts[0] { case "query": extractor = jwtFromQuery(parts[1]) case "cookie": extractor = jwtFromCookie(parts[1]) } return func(next vodka.HandlerFunc) vodka.HandlerFunc { return func(c vodka.Context) error { if config.Skipper(c) { return next(c) } auth, err := extractor(c) if err != nil { return vodka.NewHTTPError(http.StatusBadRequest, err.Error()) } token := new(jwt.Token) // Issue #647, #656 if _, ok := config.Claims.(jwt.MapClaims); ok { token, err = jwt.Parse(auth, config.keyFunc) } else { claims := reflect.ValueOf(config.Claims).Interface().(jwt.Claims) token, err = jwt.ParseWithClaims(auth, claims, config.keyFunc) } if err == nil && token.Valid { // Store user information from token into context. c.Set(config.ContextKey, token) return next(c) } return vodka.ErrUnauthorized } } }