From 0249641bda04190c16d2734b89d7fe0e00053892 Mon Sep 17 00:00:00 2001 From: Vivek Patel Date: Wed, 22 Sep 2021 00:14:50 +0530 Subject: [PATCH] Ensure extra user validation during access token validation - validate a token exist in store - when user is suspended, deleted or roles are removed - removes ALL user's tokens --- pkg/auth/jwt.go | 6 ++++ pkg/auth/middleware.go | 69 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 73 insertions(+), 2 deletions(-) diff --git a/pkg/auth/jwt.go b/pkg/auth/jwt.go index 7812566e3..eb6046aa3 100644 --- a/pkg/auth/jwt.go +++ b/pkg/auth/jwt.go @@ -25,7 +25,13 @@ type ( } tokenStore interface { + LookupUserByID(ctx context.Context, id uint64) (*types.User, error) + LookupAuthOa2tokenByAccess(ctx context.Context, access string) (*types.AuthOa2token, error) + SearchRoleMembers(ctx context.Context, f types.RoleMemberFilter) (types.RoleMemberSet, types.RoleMemberFilter, error) + CreateAuthOa2token(ctx context.Context, rr ...*types.AuthOa2token) error + DeleteAuthOA2TokenByUserID(ctx context.Context, _userID uint64) error + UpsertAuthConfirmedClient(ctx context.Context, rr ...*types.AuthConfirmedClient) error } diff --git a/pkg/auth/middleware.go b/pkg/auth/middleware.go index d6e2411b7..752032ed3 100644 --- a/pkg/auth/middleware.go +++ b/pkg/auth/middleware.go @@ -1,6 +1,8 @@ package auth import ( + "github.com/cortezaproject/corteza-server/system/types" + "github.com/go-chi/jwtauth" "net/http" "github.com/cortezaproject/corteza-server/pkg/errors" @@ -13,13 +15,25 @@ func MiddlewareValidOnly(next http.Handler) http.Handler { func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - var ctx = r.Context() + var ( + ctx = r.Context() + roles []uint64 + ) - if !GetIdentityFromContext(ctx).Valid() { + // retrieve token and claims from context + tkn, claims, err := jwtauth.FromContext(ctx) + if err != nil || !tkn.Valid { errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) return } + i := ClaimsToIdentity(claims) + if !i.Valid() { + errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) + return + } + + // check valid scope for _, s := range scope { if !CheckScope(ctx.Value(scopeCtxKey{}), s) { errors.ProperlyServeHTTP(w, r, ErrUnauthorizedScope(), false) @@ -27,7 +41,58 @@ func AccessTokenCheck(scope ...string) func(http.Handler) http.Handler { } } + // verify JWT from store + _, err = DefaultJwtStore.LookupAuthOa2tokenByAccess(ctx, tkn.Raw) + if err != nil { + errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) + return + } + + u, err := DefaultJwtStore.LookupUserByID(ctx, i.Identity()) + if err != nil { + errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) + return + } + + deleteTokens := func() { + _ = DefaultJwtStore.DeleteAuthOA2TokenByUserID(ctx, u.ID) + } + + // check if user is not suspended or deleted otherwise remove their all tokens + if u.SuspendedAt != nil || u.DeletedAt != nil { + deleteTokens() + errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) + return + } + + // check if user's role haven't changed otherwise remove their all tokens + set, _, _ := DefaultJwtStore.SearchRoleMembers(ctx, types.RoleMemberFilter{UserID: u.ID}) + _ = set.Walk(func(member *types.RoleMember) error { + roles = append(roles, member.RoleID) + return nil + }) + if !equal(roles, i.memberOf) { + deleteTokens() + errors.ProperlyServeHTTP(w, r, ErrUnauthorized(), false) + return + } + next.ServeHTTP(w, r) }) } } + +// Equal tells whether a and b contain the same elements. +// A nil argument is equivalent to an empty slice. +// fixme maybe move to utils +func equal(a, b []uint64) bool { + if len(a) != len(b) { + return false + } + for i, v := range a { + if v != b[i] { + return false + } + } + return true +}