How to trigger BasicAuth prompt in conjunction with custom HTTP error handler in golang/echo?

1k views Asked by At

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.

2

There are 2 answers

1
Abhijit-K On BEST ANSWER

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)
    }
0
Íñigo Aréjula Aísa On

I added this error handler and worked:

func CustomHTTPErrorHandler(err error, c echo.Context) {
    if c.Response().Committed {
        return
    }
    code := http.StatusInternalServerError
    if he, ok := err.(*echo.HTTPError); ok {
        code = he.Code

    }
    if code == echo.ErrUnauthorized.Code {
        err = c.JSON(code, echo.ErrUnauthorized.Message)
    }

    c.Logger().Error(err)
    errorPage := fmt.Sprintf("views/error/%d.html", code)
    if err := c.File(errorPage); err != nil {
        c.Logger().Error(err)
    }
}

The idea is to return the status 401 with the header of basic auth