middleware.go 2.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
  1. package server
  2. import (
  3. "crypto/sha256"
  4. "crypto/subtle"
  5. "net/http"
  6. )
  7. func (app *Application) BasicAuth(next http.HandlerFunc) http.HandlerFunc {
  8. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  9. // Extract the username and password from the request Authorization header.
  10. // If no Authentication header is present, or if the header value is
  11. // invalid, then the 'ok' return value will be false.
  12. username, password, ok := r.BasicAuth()
  13. if ok {
  14. // Calculate SHA-256 hashes for the provided and expected usernames
  15. // and passwords in order to have constant length values.
  16. actualUsernameHash := sha256.Sum256([]byte(username))
  17. actualPasswordHash := sha256.Sum256([]byte(password))
  18. expectedUsernameHash := sha256.Sum256([]byte(app.Username))
  19. expectedPasswordHash := sha256.Sum256([]byte(app.Password))
  20. // Use the subtle.ConstantTimeCompare() function to check if the
  21. // provided username and password hashes equal the expected
  22. // username and password hashes.
  23. // ConstantTimeCompare will return 1 if the values are equal, or 0 otherwise.
  24. // Importantly, we should do the work to evaluate both the username
  25. // and password before checking the return values, to avoid leaking information.
  26. usernameMatch := 1 == subtle.ConstantTimeCompare(actualUsernameHash[:], expectedUsernameHash[:])
  27. passwordMatch := 1 == subtle.ConstantTimeCompare(actualPasswordHash[:], expectedPasswordHash[:])
  28. // If the username and password are correct, then call the next
  29. // handler in the chain. Make sure to return afterwards, so that
  30. // none of the code below is run.
  31. if usernameMatch && passwordMatch {
  32. next.ServeHTTP(w, r)
  33. return
  34. }
  35. }
  36. // If the Authentication header is not present, is invalid, or the
  37. // username or password is wrong, then set a WWW-Authenticate header
  38. // to inform the client that we expect them to use basic authentication
  39. // and send a 401 Unauthorized response.
  40. w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
  41. http.Error(w, "Please provide an authorization", http.StatusUnauthorized)
  42. })
  43. }