pjebs / restgate

Secure Authentication for REST API endpoints.
MIT License
246 stars 23 forks source link

Integrating with Gin #4

Open dstroot opened 7 years ago

dstroot commented 7 years ago

Anyone tried integrating this with Gin? Would something like this work?

    // Create the router
    r = gin.Default()

       auth := restgate.New("X-Auth-Key", "X-Auth-Secret", restgate.Static, restgate.Config{Context: C, Key: []string{"12345"}, Secret: []string{"secret"}}))

        // Create Gin middleware - integrate unrolled secure with Gin
    authMiddleware := func() gin.HandlerFunc {
        return func(c *gin.Context) {
            err := auth.Process(c.Writer, c.Request)
            // check(err)

            // Avoid header rewrite if response is a redirection.
            if status := c.Writer.Status(); status > 300 && status < 399 {
                c.Abort()
            }
        }
    }()

    // use the new security middleware
    r.Use(authMiddleware)
pjebs commented 7 years ago

Hi @dstroot. Your code won't work but it's not hard to adapt it to work with Gin: https://github.com/gin-gonic/gin#custom-middleware

You are definitely on the right track though. I believe you'll have to somehow get gin to call: func (self *RESTGate) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc)

pjebs commented 7 years ago
rg := restgate.New("X-Auth-Key", "X-Auth-Secret", restgate.Static, restgate.Config{Context: C, Key: []string{"12345"}, Secret: []string{"secret"}}))

rgAdapter := func(c *gin.Context) {

    nextAdapter := func(http.ResponseWriter, *http.Request) {
        c.Next()
    }

    rg.ServeHTTP(c.Writer, c.Request, nextAdapter)

}

r.Use(rgAdapter)
dstroot commented 7 years ago

Awesome! This compiles and runs (I am getting output from Restgate). Now I am looking to see if there is a development config setting that will not require HTTPS in development. Thoughts?

{"code":"3","error":"Please use HTTPS connection"}{"taxProApi":"/v1/:year/taxpro/:efin"}

This code works!

        // Initialize Restgate
        rg := restgate.New("X-Auth-Key", "X-Auth-Secret", restgate.Static, restgate.Config{Key: []string{"12345"}, Secret: []string{"secret"}})

    // Create Gin middleware - integrate Restgate with Gin
    rgAdapter := func(c *gin.Context) {
        nextAdapter := func(http.ResponseWriter, *http.Request) {
            c.Next()
        }
        rg.ServeHTTP(c.Writer, c.Request, nextAdapter)
    }

    // Use Restgate with Gin
    r.Use(rgAdapter)
dstroot commented 7 years ago

Sorry - I found it.

    rg := restgate.New("X-Auth-Key", "X-Auth-Secret", restgate.Static, restgate.Config{
        Key:                []string{"12345"},
        Secret:             []string{"secret"},
        HTTPSProtectionOff: true,
    })
pjebs commented 7 years ago

When restgate fails to validate, it should not write any more to the ResponseWriter. {"code":"3","error":"Please use HTTPS connection"}{"taxProApi":"/v1/:year/taxpro/:efin"}

Something is not right there: You will have to call Abort or AbortWithError

rgAdapter := func(c *gin.Context) {
    nextCalled := false
    nextAdapter := func(http.ResponseWriter, *http.Request) {
        nextCalled = true
        c.Next()
    }
    rg.ServeHTTP(c.Writer, c.Request, nextAdapter)
    if nextCalled == false {
        c.AbortWithStatus(401)
    }
}
pjebs commented 7 years ago

       r = gin.Default()

        // Initialize Restgate
        rg := restgate.New("X-Auth-Key", "X-Auth-Secret", restgate.Static, restgate.Config{Key: []string{"12345"}, Secret: []string{"secret"}})

    // Create Gin middleware - integrate Restgate with Gin
    rgAdapter := func(c *gin.Context) {
        nextCalled := false
        nextAdapter := func(http.ResponseWriter, *http.Request) {
            nextCalled = true
            c.Next()
        }
        rg.ServeHTTP(c.Writer, c.Request, nextAdapter)
        if nextCalled == false {
            c.AbortWithStatus(401)
        }
    }

    // Use Restgate with Gin
    r.Use(rgAdapter)
ThinkCats commented 7 years ago

I want to redirect to some 401 page rather than the code:

{
  "code": "2",
  "error": "Unauthorized Access"
}

in the gin.

What should I do like this ?

ThinkCats commented 7 years ago

I tried :

if nextCalled == false {
            c.Redirect(401, "/401")
        }

But the error msg is the same as before:

{
  "code": "2",
  "error": "Unauthorized Access"
}

@pjebs

pjebs commented 7 years ago

The issue is the package was designed for making APIs. Redirects are unusual for a API response. You will have to fork the package. The reason is because rg.ServeHTTP(c.Writer, c.Request, nextAdapter) already writes to the http.ResponseWriter. Therefore c.Redirect( ) won't be able to unambiguously indicate a redirect.

pjebs commented 7 years ago

Alternatively, What you can do is create your own http.ResponseWriter and feed that into rg.ServeHTTP(OWNRESPONSEWRITER, c.Request, nextAdapter)

Then:

if nextCalled == false {
    c.Abort( )
    c.Redirect(401, "/401")
} else {
    //Copy OWNRESPONSEWRITER to ACTUAL RESPONSEWRITER
    //In practice, ignore this else clause and DO NOTHING.
}

You can use the ResponseRecorder from the net/http/httptest package if you want. It's probably easier to just create a skeleton ResponseWriter however (which would use up less memory).

ThinkCats commented 7 years ago

Ok, thank you very much ! @pjebs