1

Consider the following example which utilizes the basic auth middleware for a custom group and also uses a custom http error handler:

package main

import (
    "net/http"
    "strconv"

    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
)

// customHTTPErrorHandler utilizies custom error pages in public/error
func customHTTPErrorHandler(err error, c echo.Context) {
    code := http.StatusInternalServerError
    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code
    }
    if err := c.String(http.StatusOK, strconv.Itoa(code)); err != nil {
        c.Logger().Error(err)
    }
}

func main() {
    e := echo.New()

    e.HTTPErrorHandler = customHTTPErrorHandler

    e.Use(middleware.Logger())

    e.GET("/", func(c echo.Context) error {
        return c.String(http.StatusOK, "Index\n")
    })

    g := e.Group("/admin", middleware.BasicAuth(func(u, p string, c echo.Context) (bool, error) {
        if u == "test" && p == "test" {
            return true, nil
        }
        return false, nil
    }))

    g.GET("", func(c echo.Context) error {
        return c.String(http.StatusOK, "Admin\n")
    })

    e.Logger.Fatal(e.Start("localhost:1325"))
}

If I omit e.HTTPErrorHandler = customHTTPErrorHandler then the basic auth middleware triggers the prompt in all modern browsers. As soon as I use the custom error handler I always run into 401 without a prompt.

I know that

when basic auth middleware finds invalid credentials it returns 401 - Unauthorized error, aborting the current HTTP request.

as stated in the docs https://echo.labstack.com/guide/error-handling.

How do I get the prompt working again in this case? Am I supposed to write a custom basic auth middleware? How would I include the basic auth prompt there?

I also tried to use c.Request().BasicAuth() or c.Response().Header().Add(echo.HeaderWWWAuthenticate, `Basic realm="mydomain"`) within a e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error {}}) on top but it doesn't trigger the prompt neither.

Curl requests however giving me the expected results:

curl http://localhost:1325/admin
401
curl -u test:test http://localhost:1325/admin
Admin

But if I open up the following url in a browser http://test:test@localhost:1325/admin I run into the same error (401 Unauthorized) without the basic auth prompt.

helpexchange
  • 21
  • 2
  • 7
  • `http://test:test@localhost:1325/admin` - this URL already contains username and password. Why should the browser prompt, it already has all information. – Steffen Ullrich Dec 14 '20 at 16:50
  • Apologize for no clarification on this. It is assumed that if you provide the credentials as shown in the url, the server should be able to automatically skip the prompt and handle the request appropriately as you described. This simply wasn't the case here due to the wrong http status delivered by ```c.String()```.Thankfully @Abhijit_K provided the correct answer. – helpexchange Dec 14 '20 at 19:07

1 Answers1

1

The way is construct the error should be as below I guess. Just shown err construction to narrow down, need to perform additional checks

func customHTTPErrorHandler(err error, c echo.Context) {

    msg := echo.Map{"message": "Unauthorized"}

    err = c.JSON(401, msg)
}

You have set http status code as http.StatusOK. Below line.

if err := c.String(http.StatusOK, strconv.Itoa(code)); err != nil {

It should be 401 http.StatusUnauthorized

if err := c.String(http.StatusUnauthorized, strconv.Itoa(code)); err != nil {
        c.Logger().Error(err)
    }
Abhijit-K
  • 3,569
  • 1
  • 23
  • 31
  • This is the correct answer, thank you @Abhijit_K. Changing ```c.String(http.StatusOK, strconv.Itoa(code))``` to ```c.String(http.StatusUnauthorized, strconv.Itoa(code))``` solved the problem and now the basic auth prompt is popping up in browsers as desired. – helpexchange Dec 14 '20 at 19:11