From 0ae7f226af7ad287f361e07ef739cb53357cc602 Mon Sep 17 00:00:00 2001 From: Umputun Date: Wed, 18 Jul 2018 21:05:51 -0500 Subject: [PATCH] extend jwt claim with flags --- backend/app/rest/api/admin.go | 3 ++- backend/app/rest/api/admin_test.go | 6 +++--- backend/app/rest/api/rest_private.go | 4 ++-- backend/app/rest/auth/auth.go | 2 +- backend/app/rest/auth/auth_test.go | 2 +- backend/app/rest/auth/jwt.go | 10 +++++++++- backend/app/rest/auth/jwt_test.go | 10 +++------- backend/app/rest/auth/provider.go | 1 + 8 files changed, 22 insertions(+), 16 deletions(-) diff --git a/backend/app/rest/api/admin.go b/backend/app/rest/api/admin.go index c1f5e760..04baee07 100644 --- a/backend/app/rest/api/admin.go +++ b/backend/app/rest/api/admin.go @@ -108,7 +108,8 @@ func (a *admin) deleteMeRequestCtrl(w http.ResponseWriter, r *http.Request) { log.Printf("[INFO] delete all user comments by request for %s, site %s", claims.User.ID, claims.SiteID) - if !claims.DeleteMe { // deletme set by deleteMeCtrl, this check just to make sure we not trying to delete with leaked token + // deleteme set by deleteMeCtrl, this check just to make sure we not trying to delete with leaked token + if !claims.Flags.DeleteMe { rest.SendErrorJSON(w, r, http.StatusForbidden, errors.New("forbidden"), "can't use provided token") return } diff --git a/backend/app/rest/api/admin_test.go b/backend/app/rest/api/admin_test.go index 1e0b5a93..b8412d50 100644 --- a/backend/app/rest/api/admin_test.go +++ b/backend/app/rest/api/admin_test.go @@ -497,8 +497,8 @@ func TestAdmin_DeleteMeRequest(t *testing.T) { User: &store.User{ ID: "user1", }, - DeleteMe: true, } + claims.Flags.DeleteMe = true token, err := srv.Authenticator.JWTService.Token(&claims) assert.Nil(t, err) @@ -552,8 +552,8 @@ func TestAdmin_DeleteMeRequestFailed(t *testing.T) { User: &store.User{ ID: "user1", }, - DeleteMe: true, } + claims.Flags.DeleteMe = true token, err := srv.Authenticator.JWTService.Token(&claims) assert.Nil(t, err) @@ -578,7 +578,7 @@ func TestAdmin_DeleteMeRequestFailed(t *testing.T) { // try without deleteme flag badClaims2 := claims - badClaims2.DeleteMe = false + badClaims2.Flags.DeleteMe = false token, err = srv.Authenticator.JWTService.Token(&badClaims2) assert.Nil(t, err) req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%s/api/v1/admin/deleteme?token=%s", ts.URL, token), nil) diff --git a/backend/app/rest/api/rest_private.go b/backend/app/rest/api/rest_private.go index 8bbc0815..d8465d82 100644 --- a/backend/app/rest/api/rest_private.go +++ b/backend/app/rest/api/rest_private.go @@ -255,9 +255,9 @@ func (s *Rest) deleteMeCtrl(w http.ResponseWriter, r *http.Request) { ExpiresAt: time.Now().AddDate(0, 3, 0).Unix(), NotBefore: time.Now().Add(-1 * time.Minute).Unix(), }, - User: &user, - DeleteMe: true, // prevent this token from being used for login + User: &user, } + claims.Flags.DeleteMe = true // prevent this token from being used for login tokenStr, err := s.Authenticator.JWTService.Token(&claims) if err != nil { diff --git a/backend/app/rest/auth/auth.go b/backend/app/rest/auth/auth.go index f9a9f202..e66c2131 100644 --- a/backend/app/rest/auth/auth.go +++ b/backend/app/rest/auth/auth.go @@ -73,7 +73,7 @@ func (a *Authenticator) Auth(reqAuth bool) func(http.Handler) http.Handler { return } - if claims.DeleteMe { + if a.JWTService.HasFlags(claims) { log.Printf("[DEBUG] invalid token flags for %s/%s", claims.User.Name, claims.User.ID) http.Error(w, "Unauthorized", http.StatusUnauthorized) return diff --git a/backend/app/rest/auth/auth_test.go b/backend/app/rest/auth/auth_test.go index 39194f36..e7a50d71 100644 --- a/backend/app/rest/auth/auth_test.go +++ b/backend/app/rest/auth/auth_test.go @@ -15,7 +15,7 @@ import ( var testJwtUserBlocked = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIsImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsImFkbWluIjpmYWxzZSwiYmxvY2siOnRydWV9LCJzdGF0ZSI6IjEyMzQ1NiIsImZyb20iOiJmcm9tIn0.6P_OwGf8CUJRtvNSlW20GmaMb5pFvCNemP94fHCqb5Q" -var testJwtDeleteMe = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIsImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsImFkbWluIjpmYWxzZSwiYmxvY2siOmZhbHNlfSwiZGVsZXRlbWUiOnRydWV9.3wiT5fqDv_bzPky6-3IilU8ExfzCyvLpKDMPYOAFWEo" +var testJwtDeleteMe = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIsImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsImFkbWluIjpmYWxzZSwiYmxvY2siOmZhbHNlfSwiZmxhZ3MiOnsiZGVsZXRlbWUiOnRydWV9fQ.SLh1QpFytWZqcT99VgcdAOtgFKhvpKCcZwqWTvAd63g" func TestAuthJWTCookie(t *testing.T) { a := Authenticator{DevPasswd: "123456", JWTService: NewJWT("xyz 12345", false, time.Hour, time.Hour), diff --git a/backend/app/rest/auth/jwt.go b/backend/app/rest/auth/jwt.go index 0b1673fe..54855432 100644 --- a/backend/app/rest/auth/jwt.go +++ b/backend/app/rest/auth/jwt.go @@ -31,7 +31,10 @@ type CustomClaims struct { SessionOnly bool `json:"sess_only,omitempty"` // flags indicate different uses - DeleteMe bool `json:"deleteme,omitempty"` + Flags struct { + Login bool `json:"login,omitempty"` + DeleteMe bool `json:"deleteme,omitempty"` + } `json:"flags,omitempty"` } const jwtCookieName = "JWT" @@ -60,6 +63,11 @@ func (j *JWT) Token(claims *CustomClaims) (string, error) { return tokenString, nil } +// HasFlags indicates presense of special flags +func (j *JWT) HasFlags(claims *CustomClaims) bool { + return claims.Flags.DeleteMe || claims.Flags.Login +} + // Parse token string and verify. Not checking for expiration func (j *JWT) Parse(tokenString string) (*CustomClaims, error) { parser := jwt.Parser{SkipClaimsValidation: true} // allow parsing of expired tokens diff --git a/backend/app/rest/auth/jwt_test.go b/backend/app/rest/auth/jwt_test.go index bb5eb084..0795809b 100644 --- a/backend/app/rest/auth/jwt_test.go +++ b/backend/app/rest/auth/jwt_test.go @@ -14,13 +14,9 @@ import ( "github.com/umputun/remark/backend/app/store" ) -var testJwtValid = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCI" + - "sImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZS" + - "I6IiIsImFkbWluIjpmYWxzZX0sInN0YXRlIjoiMTIzNDU2IiwiZnJvbSI6ImZyb20ifQ._loFgh3g45gr9TtGqvM3N584I_6EHEOJnYb6Py84stQ" +var testJwtValid = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIsImlzcyI6InJlb" + "WFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsImFkbWluIjpmYWxzZX0" + "sInN0YXRlIjoiMTIzNDU2IiwiZnJvbSI6ImZyb20iLCJmbGFncyI6e319.E2Blxqo1wsY855q258c0obxFJ1lgJciv1av1ewzlJBs" -var testJwtValidSess = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIsImlzcyI6In" + - "JlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsIm" + - "FkbWluIjpmYWxzZX0sInN0YXRlIjoiMTIzNDU2IiwiZnJvbSI6ImZyb20iLCJzZXNzX29ubHkiOnRydWV9.p6w0sM_NYaRuyhyA9jqfWlB5cx1vZPGhXGC5geSX7nA" +var testJwtValidSess = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3ODkxOTE4MjIsImp0aSI6InJhbmRvbSBpZCIs" + "ImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiIsImFk" + "bWluIjpmYWxzZX0sInN0YXRlIjoiMTIzNDU2IiwiZnJvbSI6ImZyb20iLCJzZXNzX29ubHkiOnRydWUsImZsYWdzIjp7fX0." + "nKhehF1Xiome1yK1ewfOiIsrATvq7Tx7p1BCSJqKHuo" var testJwtExpired = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1MjY4ODc4MjIsImp0aSI6InJhbmRvbSBpZCIs" + "ImlzcyI6InJlbWFyazQyIiwibmJmIjoxNTI2ODg0MjIyLCJ1c2VyIjp7Im5hbWUiOiJuYW1lMSIsImlkIjoiaWQxIiwicGljdHVyZSI6IiI" + @@ -89,9 +85,9 @@ func TestJWT_Set(t *testing.T) { ExpiresAt: time.Date(2058, 5, 21, 1, 30, 22, 0, time.Local).Unix(), NotBefore: time.Date(2018, 5, 21, 1, 30, 22, 0, time.Local).Unix(), }, + SessionOnly: false, } - claims.SessionOnly = false rr := httptest.NewRecorder() err := j.Set(rr, claims, claims.SessionOnly) assert.Nil(t, err) diff --git a/backend/app/rest/auth/provider.go b/backend/app/rest/auth/provider.go index 40946429..30f87739 100644 --- a/backend/app/rest/auth/provider.go +++ b/backend/app/rest/auth/provider.go @@ -99,6 +99,7 @@ func (p Provider) loginHandler(w http.ResponseWriter, r *http.Request) { NotBefore: time.Now().Add(-1 * time.Minute).Unix(), }, } + claims.Flags.Login = true if err := p.JwtService.Set(w, &claims, false); err != nil { rest.SendErrorJSON(w, r, http.StatusInternalServerError, err, "failed to set jwt")