volatiletech / authboss

The boss of http auth.
MIT License
3.76k stars 203 forks source link

Question: Custom ClientStateReadWriter implementation #307

Open thomasvvugt opened 3 years ago

thomasvvugt commented 3 years ago

Hi all,

I am trying to combine authboss into a popular upcoming Go web-framework, Fiber and GORM to improve my current authentication implementation.

Fiber was built with FastHTTP as its core component with the ease-of-use and API-compatibility in mind of express. Fiber has it's own session package, based on FastHTTP's session package. Also, Fiber has developed methods to set and retrieve cookie values.

However, when creating the required SessionState and CookieState storage configuration objects, I figured the ClientStateReadWriter interface only allows Reading and Writing the default http Request and Response package; https://github.com/volatiletech/authboss/blob/master/client_state.go#L79

I would love to implement Fiber's official session middleware and cookie methods to use authboss for authentication, but I have no clue on where to start regarding the implementation of the SessionState and CookieState or maybe I am even the purpose of the Middleware2() method. I have started development for this implementation on the develop branch of my personal repo.

Would you be able to give me a better approach into creating a flawless integration for the authboss package, using Fiber's Ctx (Context) object and it's session middleware? Thanks so much in advance, I would really love to collaborate on this further!

ibraheemdev commented 3 years ago

You can look at the authboss client state, which is an implementation of the SessionState and CookieState storage configuration objects using the Gorilla Securecookie and Sessions packages. That repo should be a good starting point for your development.

thomasvvugt commented 3 years ago

Hi @ibraheemdev and thanks for pointing me to the authboss client state repo.

I have looked at these implementations for the SessionState and CookieState, where both objects use the ClientStateReadWriter interface.

This interface ensures both objects are reading and writing states using the following structure;

Is there a possibility to i.e. port Fiber's Ctx or FastHTTP's Context object to be able to comply to the ClientStateReadWriter interface? Fiber has an adaptor middleware to convert it's Ctx to an http.Handler type, however I cannot seem to find an example on how to use custom packages instead of the http package to use authboss in a different framework.

Thanks for the help!

ibraheemdev commented 3 years ago

I'm not familiar with Fiber or FastHTTP context, but it should be as simple as creating wrapper functions to satisfy the ClientStateReadWriter Interface. I'll try to look into it when I have the time.

frederikhors commented 3 years ago

@thomasvvugt did you find a solution for this?

thomasvvugt commented 3 years ago

Hi @frederikhors, unfortunately not. AuthBoss's implementation for the ClientStateReadWriter takes an http.Request and http.ResponseWriter to to create a cookie storer. Custom implementations like Fasthttp or Fiber really require a different interface since they use their own Context handling.

ibraheemdev commented 3 years ago

@thomasvvugt Have you looked into fasthttpadaptor? It allows you to convert net/http requests to fasthttp (fiber) requests. Something like this:

import (
    "github.com/gofiber/fiber"
    "github.com/valyala/fasthttp/fasthttpadaptor"
    "net/http"
)

...

g.Get("/", WrapHandler(pprof.Index))

func WrapHandler(f func(http.ResponseWriter, *http.Request)) func(ctx *fiber.Ctx) {
    return func(ctx *fiber.Ctx) {
        fasthttpadaptor.NewFastHTTPHandler(http.HandlerFunc(f))(ctx.Fasthttp)
    }
}

code block originally from this issue

Edit: You should probably use gofiber/adaptor, which is a convenience layer on top of fasthttpadaptor.

frederikhors commented 3 years ago

@ibraheemdev I tried very hard, but I can't figure out how to do.

This is LoadClientStateMiddleware:

func (a *Authboss) LoadClientStateMiddleware(h http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        writer := a.NewResponse(w)
        request, err := a.LoadClientState(writer, r)
        if err != nil {
            logger := a.RequestLogger(r)
            logger.Errorf("failed to load client state %+v", err)

            w.WriteHeader(http.StatusInternalServerError)
            return
        }

        h.ServeHTTP(writer, request)
    })
}

This is was I also tried:

    app := fiber.New()

    app.Use(WrapHandler(authboss.LoadClientStateMiddleware))

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello, World 👋!")
    })

    _ = app.Listen(":3000")
}

func WrapHandler(f func(http.Handler) http.Handler) func(ctx *fiber.Ctx) {
    return func(ctx *fiber.Ctx) {
        //fasthttpadaptor.NewFastHTTPHandler(http.HandlerFunc(f))(ctx.Fasthttp)
        adaptor.HTTPHandlerFunc(http.HandlerFunc(f))(ctx)
    }

But this is simply wrong.

ibraheemdev commented 3 years ago

Well, this seems to work:

func main() {
    // New fiber app
    app := fiber.New()

    app.Get("/ping", adaptor.HTTPHandler(logMiddleware(adaptor.FiberHandler(ping))))

    // Listen on port 3000
    app.Listen(":3000")
}

func ping(c *fiber.Ctx) error {
    return c.SendString("pong")
}

func logMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        println("logging...")
        next.ServeHTTP(w, r)
    })
}

func greet(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello World!")
}

So, I'm sure it's possible. It'll just take some fiddling with.

frederikhors commented 3 years ago

Oh wow. Thanks for your commitment.

Your code works.

But what I don't understand is how to use LoadClientStateMiddleware with app.Use().

As stated in Authboss docs, LoadClientStateMiddleware needs to wrap each route (is a middleware) so i cannot use exaclty this example because logMiddleware is called with adaptor.FiberHandler(ping) in it.

What we need is something like:

app := fiber.New()

app.Use(adaptor.HTTPHandlerORSOMETHINGIDONTUNDERSTAND(initializedAuthboss.LoadClientStateMiddleware))

app.Get("/ping", ping)

app.Listen(":3000")

This is the problem.

Thanks again.

frederikhors commented 3 years ago

Using the below code I get this error:

panic: use: invalid handler func(http.Handler) http.Handler
func main() {
  app := fiber.New()

  app.Use(logMiddleware)

  app.Get("/ping", ping)

  _ = app.Listen(":3000")
}

func ping(c *fiber.Ctx) error {
    return c.SendString("pong")
}

func logMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        println("logging...")
        next.ServeHTTP(w, r)
    })
}
ibraheemdev commented 3 years ago

I'm not really familiar with fiber and fasthttp. I opened up an issue there (gofiber/fiber#943), hopefully they can help you out

frederikhors commented 3 years ago

Thanks to @arsmn we can make it work now: https://github.com/gofiber/adaptor/issues/27#issuecomment-717486233.

Two remaining issues:

  1. LoadClientStateMiddleware sets context value which does not transfer to Fiber handler; can you help us with this, @aarondl?

  2. how to transform this code mux.Mount("/auth", http.StripPrefix("/auth", ab.Config.Core.Router)) for Fiber; this code comes from here: https://github.com/volatiletech/authboss-sample/blob/master/blog.go#L274.

I'm trying with this code which compiles:

func main() {
    ab := SetupAuthboss()

    app := fiber.New()

    app.Use(adaptor.HTTPMiddleware(ab.LoadClientStateMiddleware), remember.Middleware(ab))

    app.Group("/auth", adaptor.HTTPHandler(http.StripPrefix("/auth", ab.Config.Core.Router)))

    _ = app.Listen(":3000")
}

but crashes at runtime:

panic: use: invalid handler func(http.Handler) http.Handler

goroutine 1 [running]:
github.com/gofiber/fiber/v2.(*App).Use(0xc00011b200, 0xc00010ff58, 0x2, 0x2, 0x1, 0xbdcd94)
  C:/go/pkg/mod/github.com/gofiber/fiber/v2@v2.1.1/app.go:391 +0x2d7
main.main()
  C:/projects/go/fiberAndAuthboss/main.go:18 +0x129
exit status 2

FULL REPRODUCTION PROJECT HERE: https://github.com/frederikhors/fiber-and-authboss

aarondl commented 3 years ago

Hi @frederikhors. Not sure I can help out here. Never used fiber before.

gaby commented 3 weeks ago

Was there any progress on this? I'm looking into how to integrate authboss natively in GoFiber. Biggest program is the lack of fasthttp support.

While the adaptor middleware should work, it does introduce overhead.