basic_auth.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. package middleware
  2. import (
  3. "encoding/base64"
  4. "strconv"
  5. "strings"
  6. "github.com/labstack/echo"
  7. )
  8. type (
  9. // BasicAuthConfig defines the config for BasicAuth middleware.
  10. BasicAuthConfig struct {
  11. // Skipper defines a function to skip middleware.
  12. Skipper Skipper
  13. // Validator is a function to validate BasicAuth credentials.
  14. // Required.
  15. Validator BasicAuthValidator
  16. // Realm is a string to define realm attribute of BasicAuth.
  17. // Default value "Restricted".
  18. Realm string
  19. }
  20. // BasicAuthValidator defines a function to validate BasicAuth credentials.
  21. BasicAuthValidator func(string, string, echo.Context) (bool, error)
  22. )
  23. const (
  24. basic = "basic"
  25. defaultRealm = "Restricted"
  26. )
  27. var (
  28. // DefaultBasicAuthConfig is the default BasicAuth middleware config.
  29. DefaultBasicAuthConfig = BasicAuthConfig{
  30. Skipper: DefaultSkipper,
  31. Realm: defaultRealm,
  32. }
  33. )
  34. // BasicAuth returns an BasicAuth middleware.
  35. //
  36. // For valid credentials it calls the next handler.
  37. // For missing or invalid credentials, it sends "401 - Unauthorized" response.
  38. func BasicAuth(fn BasicAuthValidator) echo.MiddlewareFunc {
  39. c := DefaultBasicAuthConfig
  40. c.Validator = fn
  41. return BasicAuthWithConfig(c)
  42. }
  43. // BasicAuthWithConfig returns an BasicAuth middleware with config.
  44. // See `BasicAuth()`.
  45. func BasicAuthWithConfig(config BasicAuthConfig) echo.MiddlewareFunc {
  46. // Defaults
  47. if config.Validator == nil {
  48. panic("echo: basic-auth middleware requires a validator function")
  49. }
  50. if config.Skipper == nil {
  51. config.Skipper = DefaultBasicAuthConfig.Skipper
  52. }
  53. if config.Realm == "" {
  54. config.Realm = defaultRealm
  55. }
  56. return func(next echo.HandlerFunc) echo.HandlerFunc {
  57. return func(c echo.Context) error {
  58. if config.Skipper(c) {
  59. return next(c)
  60. }
  61. auth := c.Request().Header.Get(echo.HeaderAuthorization)
  62. l := len(basic)
  63. if len(auth) > l+1 && strings.ToLower(auth[:l]) == basic {
  64. b, err := base64.StdEncoding.DecodeString(auth[l+1:])
  65. if err != nil {
  66. return err
  67. }
  68. cred := string(b)
  69. for i := 0; i < len(cred); i++ {
  70. if cred[i] == ':' {
  71. // Verify credentials
  72. valid, err := config.Validator(cred[:i], cred[i+1:], c)
  73. if err != nil {
  74. return err
  75. } else if valid {
  76. return next(c)
  77. }
  78. break
  79. }
  80. }
  81. }
  82. realm := defaultRealm
  83. if config.Realm != defaultRealm {
  84. realm = strconv.Quote(config.Realm)
  85. }
  86. // Need to return `401` for browsers to pop-up login box.
  87. c.Response().Header().Set(echo.HeaderWWWAuthenticate, basic+" realm="+realm)
  88. return echo.ErrUnauthorized
  89. }
  90. }
  91. }