Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Custom ClientStateReadWriter implementation #307

Open
thomasvvugt opened this issue Aug 15, 2020 · 14 comments
Open

Question: Custom ClientStateReadWriter implementation #307

thomasvvugt opened this issue Aug 15, 2020 · 14 comments

Comments

@thomasvvugt
Copy link

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
Copy link
Contributor

ibraheemdev commented Aug 17, 2020

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
Copy link
Author

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;

  • ReadState(*http.Request) (ClientState, error)
  • WriteState(http.ResponseWriter, ClientState, []ClientStateEvent) error
    Which takes the default http Request and Response package.

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
Copy link
Contributor

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
Copy link
Contributor

@thomasvvugt did you find a solution for this?

@thomasvvugt
Copy link
Author

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
Copy link
Contributor

ibraheemdev commented Oct 19, 2020

@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
Copy link
Contributor

frederikhors commented Oct 19, 2020

@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
Copy link
Contributor

ibraheemdev commented Oct 20, 2020

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

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
Copy link
Contributor

Thanks to @arsmn we can make it work now: gofiber/adaptor#27 (comment).

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
Copy link
Member

aarondl commented Nov 4, 2020

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

@gaby
Copy link

gaby commented Jun 25, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants