gin-jwt icon indicating copy to clipboard operation
gin-jwt copied to clipboard

different Authorizator depending on Router Groups

Open philippecarle opened this issue 8 years ago • 5 comments

Hi,

(thx for having merged so quickly my PR on Context injection ^^)

I'm facing an issue, and I think it may be a functional / logical problem in Authorization implementation and / or limitations cause by lack of informations about groups in gin.Context.

For example, here are my groups definitions for admin/users :

    admin := r.Group("/admin")
    admin.Use(authMiddleware.MiddlewareFunc())
    {
        users := admin.Group("/users")
        users.GET("/:username", user.ByUsernameHandler)
        users.POST("", registration.RegisterHandler)
        users.GET("", user.AllUsersHandler)
    }

By using your Authorizator callback, I have to be aware of two informations :

  • roles of user (stored in my DB)
  • which route matches to the request and in which group(s) it belongs

Each leads me to an issue :

  • User roles can be accessed only by querying for the second time (it has already been queried in Authenticator Handler). I've been trying to set user in context with c.Set("User", user) in Authenticator and access it in Authorizator, but unsuccessfully, c.Get return false for "exists" return param…
  • gin.Context does not contain Group… Just handler name…

Should I implement my own solution like writing a specific Middleware, with an string argument telling me what group is called ? Or is there any more elegant way to fix this ?

philippecarle avatar Apr 18 '16 15:04 philippecarle

maybe you can use belown method

func xxAuthorizator func(userID string, c *gin.Context) bool {
    claims := jwt.ExtractClaims(c)
    //.... get token and do your things
}

gonboy avatar Dec 13 '17 00:12 gonboy

Hi @philippecarle, or anyone else, did you find an elegant solution to this?

I also would like to pass a different Authorizator function for different groups, ideally without creating multiple middlewares.

Thanks!

arrwhidev avatar Dec 01 '18 20:12 arrwhidev

@arrwhidev you can create a single middleware and switch between its parameter:

func GroupAuthorizator(group string) gin.HandlerFunc {
	return func(c *gin.Context) {
		switch group {
		case "product":
			usr, _ := c.Get("id")
			fmt.Println(usr.(*models.User).UserName)
			c.Next()
                        return
		default:
			jwt.Unauthorized(c, http.StatusForbidden, jwt.HTTPStatusMessageFunc(jwt.ErrForbidden, c))
		}
	}
}

And then, create your gin group router like this:

t := &ProductRouter{}
tR := r.Group("/product")
tR.Use(jwt.MiddlewareFunc(), GroupAuthorizator("product"))
tR.GET("/", t.GetProduct)

ghost avatar Mar 15 '19 20:03 ghost

@alcmoraes, could you be more detailed ? how to distinguish normal user, admin user, guest user?

usr, _ := c.Get("id")
fmt.Println(usr.(*models.User).UserName)
c.Next()
return

Could you explain these lines ?

tomriddle1234 avatar May 18 '19 03:05 tomriddle1234

Currently I hacked in the gin-jwt source code, like this, to by pass the authenticator function for normal users, so I achieved 3 roles, normal user, admin, and guest without login

// NormalMiddlewareFunc makes GinJWTMiddleware implement the Middleware interface by pass the admin identity check
func (mw *GinJWTMiddleware) NormalMiddlewareFunc() gin.HandlerFunc {
	return func(c *gin.Context) {
		mw.normalMiddlewareImpl(c)
	}
}

func (mw *GinJWTMiddleware) normalMiddlewareImpl(c *gin.Context) {
	claims, err := mw.GetClaimsFromJWT(c)
	if err != nil {
		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(err, c))
		return
	}

	if claims["exp"] == nil {
		mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrMissingExpField, c))
		return
	}

	if _, ok := claims["exp"].(float64); !ok {
		mw.unauthorized(c, http.StatusBadRequest, mw.HTTPStatusMessageFunc(ErrWrongFormatOfExp, c))
		return
	}

	if int64(claims["exp"].(float64)) < mw.TimeFunc().Unix() {
		mw.unauthorized(c, http.StatusUnauthorized, mw.HTTPStatusMessageFunc(ErrExpiredToken, c))
		return
	}

	c.Set("JWT_PAYLOAD", claims)
	identity := mw.IdentityHandler(c)

	if identity != nil {
		c.Set(mw.IdentityKey, identity)
	}

	c.Next()
}

Then I used such middleware function like,

	knowledgeRoutes := router.Group("/data")
	{
		knowledgeRoutes.Use(jwtMiddleware.NormalMiddlewareFunc())
		{
			//these fetch functions record statistics
			knowledgeRoutes.GET("/getknowledgelist",  getKnowledgeListHandler)
			knowledgeRoutes.GET("/getallknowledge", getAllKnowledgeHandler)
			knowledgeRoutes.GET("/getknowledge/:name", getKnowledgeByNameHandler)
			knowledgeRoutes.GET("/getknowledgelogo/:name", getKnowledgeLogoHandler)
			knowledgeRoutes.POST("/searchknowledge/:keyword", searchKnowledgeKeywordHandler)
		}

	}

tomriddle1234 avatar May 18 '19 04:05 tomriddle1234