basic_auth.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package httpauth
  2. import (
  3. "bytes"
  4. "crypto/sha256"
  5. "crypto/subtle"
  6. "encoding/base64"
  7. "fmt"
  8. "net/http"
  9. "strings"
  10. )
  11. type basicAuth struct {
  12. h http.Handler
  13. opts AuthOptions
  14. }
  15. // AuthOptions stores the configuration for HTTP Basic Authentication.
  16. //
  17. // A http.Handler may also be passed to UnauthorizedHandler to override the
  18. // default error handler if you wish to serve a custom template/response.
  19. type AuthOptions struct {
  20. Realm string
  21. User string
  22. Password string
  23. AuthFunc func(string, string, *http.Request) bool
  24. UnauthorizedHandler http.Handler
  25. }
  26. // Satisfies the http.Handler interface for basicAuth.
  27. func (b basicAuth) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  28. // Check if we have a user-provided error handler, else set a default
  29. if b.opts.UnauthorizedHandler == nil {
  30. b.opts.UnauthorizedHandler = http.HandlerFunc(defaultUnauthorizedHandler)
  31. }
  32. // Check that the provided details match
  33. if b.authenticate(r) == false {
  34. b.requestAuth(w, r)
  35. return
  36. }
  37. // Call the next handler on success.
  38. b.h.ServeHTTP(w, r)
  39. }
  40. // authenticate retrieves and then validates the user:password combination provided in
  41. // the request header. Returns 'false' if the user has not successfully authenticated.
  42. func (b *basicAuth) authenticate(r *http.Request) bool {
  43. const basicScheme string = "Basic "
  44. if r == nil {
  45. return false
  46. }
  47. // In simple mode, prevent authentication with empty credentials if User is
  48. // not set. Allow empty passwords to support non-password use-cases.
  49. if b.opts.AuthFunc == nil && b.opts.User == "" {
  50. return false
  51. }
  52. // Confirm the request is sending Basic Authentication credentials.
  53. auth := r.Header.Get("Authorization")
  54. if !strings.HasPrefix(auth, basicScheme) {
  55. return false
  56. }
  57. // Get the plain-text username and password from the request.
  58. // The first six characters are skipped - e.g. "Basic ".
  59. str, err := base64.StdEncoding.DecodeString(auth[len(basicScheme):])
  60. if err != nil {
  61. return false
  62. }
  63. // Split on the first ":" character only, with any subsequent colons assumed to be part
  64. // of the password. Note that the RFC2617 standard does not place any limitations on
  65. // allowable characters in the password.
  66. creds := bytes.SplitN(str, []byte(":"), 2)
  67. if len(creds) != 2 {
  68. return false
  69. }
  70. givenUser := string(creds[0])
  71. givenPass := string(creds[1])
  72. // Default to Simple mode if no AuthFunc is defined.
  73. if b.opts.AuthFunc == nil {
  74. b.opts.AuthFunc = b.simpleBasicAuthFunc
  75. }
  76. return b.opts.AuthFunc(givenUser, givenPass, r)
  77. }
  78. // simpleBasicAuthFunc authenticates the supplied username and password against
  79. // the User and Password set in the Options struct.
  80. func (b *basicAuth) simpleBasicAuthFunc(user, pass string, r *http.Request) bool {
  81. // Equalize lengths of supplied and required credentials
  82. // by hashing them
  83. givenUser := sha256.Sum256([]byte(user))
  84. givenPass := sha256.Sum256([]byte(pass))
  85. requiredUser := sha256.Sum256([]byte(b.opts.User))
  86. requiredPass := sha256.Sum256([]byte(b.opts.Password))
  87. // Compare the supplied credentials to those set in our options
  88. if subtle.ConstantTimeCompare(givenUser[:], requiredUser[:]) == 1 &&
  89. subtle.ConstantTimeCompare(givenPass[:], requiredPass[:]) == 1 {
  90. return true
  91. }
  92. return false
  93. }
  94. // Require authentication, and serve our error handler otherwise.
  95. func (b *basicAuth) requestAuth(w http.ResponseWriter, r *http.Request) {
  96. w.Header().Set("WWW-Authenticate", fmt.Sprintf(`Basic realm=%q`, b.opts.Realm))
  97. b.opts.UnauthorizedHandler.ServeHTTP(w, r)
  98. }
  99. // defaultUnauthorizedHandler provides a default HTTP 401 Unauthorized response.
  100. func defaultUnauthorizedHandler(w http.ResponseWriter, r *http.Request) {
  101. http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
  102. }
  103. // BasicAuth provides HTTP middleware for protecting URIs with HTTP Basic Authentication
  104. // as per RFC 2617. The server authenticates a user:password combination provided in the
  105. // "Authorization" HTTP header.
  106. //
  107. // Example:
  108. //
  109. // package main
  110. //
  111. // import(
  112. // "net/http"
  113. // "github.com/zenazn/goji"
  114. // "github.com/goji/httpauth"
  115. // )
  116. //
  117. // func main() {
  118. // basicOpts := httpauth.AuthOptions{
  119. // Realm: "Restricted",
  120. // User: "Dave",
  121. // Password: "ClearText",
  122. // }
  123. //
  124. // goji.Use(httpauth.BasicAuth(basicOpts), SomeOtherMiddleware)
  125. // goji.Get("/thing", myHandler)
  126. // }
  127. //
  128. // Note: HTTP Basic Authentication credentials are sent in plain text, and therefore it does
  129. // not make for a wholly secure authentication mechanism. You should serve your content over
  130. // HTTPS to mitigate this, noting that "Basic Authentication" is meant to be just that: basic!
  131. func BasicAuth(o AuthOptions) func(http.Handler) http.Handler {
  132. fn := func(h http.Handler) http.Handler {
  133. return basicAuth{h, o}
  134. }
  135. return fn
  136. }
  137. // SimpleBasicAuth is a convenience wrapper around BasicAuth. It takes a user and password, and
  138. // returns a pre-configured BasicAuth handler using the "Restricted" realm and a default 401 handler.
  139. //
  140. // Example:
  141. //
  142. // package main
  143. //
  144. // import(
  145. // "net/http"
  146. // "github.com/zenazn/goji/web/httpauth"
  147. // )
  148. //
  149. // func main() {
  150. //
  151. // goji.Use(httpauth.SimpleBasicAuth("dave", "somepassword"), SomeOtherMiddleware)
  152. // goji.Get("/thing", myHandler)
  153. // }
  154. //
  155. func SimpleBasicAuth(user, password string) func(http.Handler) http.Handler {
  156. opts := AuthOptions{
  157. Realm: "Restricted",
  158. User: user,
  159. Password: password,
  160. }
  161. return BasicAuth(opts)
  162. }