Another set of after-update JWT fixes
This commit is contained in:
+2
-3
@@ -337,6 +337,8 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
|
||||
app.WsServer = websocket.Server(app.Log, app.Opt.Websocket)
|
||||
|
||||
corredor.Service().SetAuthTokenMaker(app.jwt)
|
||||
|
||||
ctx = actionlog.RequestOriginToContext(ctx, actionlog.RequestOrigin_APP_Init)
|
||||
defer sentry.Recover()
|
||||
|
||||
@@ -414,9 +416,6 @@ func (app *CortezaApp) InitServices(ctx context.Context) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
//@todo remove vv
|
||||
//auth.SetJWTStore(app.Store)
|
||||
|
||||
corredor.Service().SetUserFinder(sysService.DefaultUser)
|
||||
corredor.Service().SetRoleFinder(sysService.DefaultRole)
|
||||
|
||||
|
||||
@@ -10,9 +10,11 @@ import (
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-chi/jwtauth"
|
||||
oauth2errors "github.com/go-oauth2/oauth2/v4/errors"
|
||||
"github.com/lestrrat-go/jwx/jwk"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/auth/oauth2"
|
||||
"github.com/cortezaproject/corteza-server/auth/request"
|
||||
@@ -21,7 +23,6 @@ import (
|
||||
systemService "github.com/cortezaproject/corteza-server/system/service"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
oauth2def "github.com/go-oauth2/oauth2/v4"
|
||||
oauth2errors "github.com/go-oauth2/oauth2/v4/errors"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
@@ -168,8 +169,27 @@ func (h AuthHandlers) oauth2Token(req *request.AuthReq) (err error) {
|
||||
return h.handleTokenRequest(req, client)
|
||||
}
|
||||
|
||||
// oauth2Info handler validates token and responds with decoded claims
|
||||
func (h AuthHandlers) oauth2Info(w http.ResponseWriter, r *http.Request) {
|
||||
ti, err := h.OAuth2.ValidationBearerToken(r)
|
||||
var (
|
||||
jt jwt.Token
|
||||
claims map[string]interface{}
|
||||
|
||||
// scope is intentionally left empty
|
||||
scope = make([]string, 0)
|
||||
)
|
||||
|
||||
err := func() (err error) {
|
||||
if jt, claims, err = jwtauth.FromContext(r.Context()); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = auth.JWT().Validate(r.Context(), jt, scope...); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, context.Canceled) {
|
||||
@@ -203,20 +223,7 @@ func (h AuthHandlers) oauth2Info(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"active": true,
|
||||
"scope": ti.GetScope(),
|
||||
"client_id": ti.GetClientID(),
|
||||
"exp": int64(ti.GetAccessCreateAt().Add(ti.GetAccessExpiresIn()).Sub(time.Now()).Seconds()),
|
||||
"aud": ti.GetClientID(),
|
||||
}
|
||||
|
||||
SubSplit(ti, data)
|
||||
if err = Profile(r.Context(), ti, data); err != nil {
|
||||
h.Log.Error("failed to add profile data", zap.Error(err))
|
||||
}
|
||||
|
||||
_ = json.NewEncoder(w).Encode(data)
|
||||
_ = json.NewEncoder(w).Encode(claims)
|
||||
}
|
||||
|
||||
// oauth2authorizeDefaultClient acts as a proxy for default client
|
||||
@@ -359,8 +366,6 @@ func (h AuthHandlers) loadRequestedClient(req *request.AuthReq) (client *types.A
|
||||
}
|
||||
|
||||
func (h AuthHandlers) handleTokenRequest(req *request.AuthReq, client *types.AuthClient) error {
|
||||
req.Status = -1
|
||||
|
||||
var (
|
||||
r = req.Request
|
||||
w = req.Response
|
||||
@@ -402,6 +407,14 @@ func (h AuthHandlers) handleTokenRequest(req *request.AuthReq, client *types.Aut
|
||||
return h.tokenError(w, err)
|
||||
}
|
||||
|
||||
var (
|
||||
user = req.AuthUser.User.Clone()
|
||||
signed []byte
|
||||
)
|
||||
|
||||
signed, err = auth.JWT().Sign(ti.GetAccess(), user, client.ID, strings.Split(ti.GetScope(), " ")...)
|
||||
ti.SetAccess(string(signed))
|
||||
|
||||
return token(w, h.OAuth2.GetTokenData(ti), nil)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
package oauth2
|
||||
|
||||
//import (
|
||||
// "context"
|
||||
// "strings"
|
||||
//
|
||||
// "github.com/cortezaproject/corteza-server/pkg/auth"
|
||||
// "github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
// "github.com/cortezaproject/corteza-server/pkg/rand"
|
||||
// "github.com/go-oauth2/oauth2/v4"
|
||||
// "github.com/spf13/cast"
|
||||
//)
|
||||
//
|
||||
//// JWTAccessGenerate generate the jwt access token
|
||||
//type (
|
||||
// tokenGenerator interface {
|
||||
// Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
// }
|
||||
//
|
||||
// JWTAccessGenerate struct {
|
||||
// tm tokenGenerator
|
||||
// }
|
||||
//)
|
||||
//
|
||||
//// NewJWTAccessGenerate create to generate the jwt access token instance
|
||||
////
|
||||
//// @todo move this to pkg/auth (??) so it can be re-used
|
||||
//func NewJWTAccessGenerate(tg tokenGenerator) *JWTAccessGenerate {
|
||||
// return &JWTAccessGenerate{tg}
|
||||
//}
|
||||
//
|
||||
//// Token based on the UUID generated token
|
||||
//func (a *JWTAccessGenerate) Token(ctx context.Context, data *oauth2.GenerateBasic, isGenRefresh bool) (_ string, refresh string, err error) {
|
||||
// var (
|
||||
// user auth.Identifiable
|
||||
// rawToken []byte
|
||||
// )
|
||||
//
|
||||
// {
|
||||
// // extract user ID and roles from a space-delimited list of IDs stored in userID
|
||||
// userIdWithRoles := strings.Split(data.TokenInfo.GetUserID(), " ")
|
||||
// if len(userIdWithRoles) == 1 {
|
||||
// user = auth.Authenticated(cast.ToUint64(userIdWithRoles[0]))
|
||||
// } else {
|
||||
// user = auth.Authenticated(
|
||||
// cast.ToUint64(userIdWithRoles[0]),
|
||||
// payload.ParseUint64s(userIdWithRoles)...,
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// rawToken, err = a.tm.Generate(ctx, user, cast.ToUint64(data.Client.GetID()), data.TokenInfo.GetScope())
|
||||
// if err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if isGenRefresh {
|
||||
// refresh = string(rand.Bytes(48))
|
||||
// }
|
||||
//
|
||||
// return string(rawToken), refresh, nil
|
||||
//}
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
|
||||
func NewUserAuthorizer(sm *request.SessionManager, loginURL, clientAuthURL string) server.UserAuthorizationHandler {
|
||||
return func(w http.ResponseWriter, r *http.Request) (identity string, err error) {
|
||||
|
||||
var (
|
||||
ses = sm.Get(r)
|
||||
au = request.GetAuthUser(ses)
|
||||
@@ -40,7 +39,7 @@ func NewUserAuthorizer(sm *request.SessionManager, loginURL, clientAuthURL strin
|
||||
}
|
||||
}
|
||||
|
||||
var roles = request.GetRoleMemberships(ses)
|
||||
roles := au.User.Roles()
|
||||
if client.Security != nil {
|
||||
// filter user's roles with client security settings
|
||||
roles = internalAuth.ApplyRoleSecurity(
|
||||
|
||||
+9
-12
@@ -1,9 +1,10 @@
|
||||
package request
|
||||
|
||||
import (
|
||||
"net/url"
|
||||
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/gorilla/sessions"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -23,11 +24,16 @@ func GetAuthUser(ses *sessions.Session) *authUser {
|
||||
return nil
|
||||
}
|
||||
|
||||
return val.(*authUser)
|
||||
au := val.(*authUser)
|
||||
if au.User != nil {
|
||||
au.User.SetRoles(getRoleMemberships(ses)...)
|
||||
}
|
||||
|
||||
return au
|
||||
}
|
||||
|
||||
// GetRoleMemberships is wrapper to get value from session
|
||||
func GetRoleMemberships(ses *sessions.Session) []uint64 {
|
||||
func getRoleMemberships(ses *sessions.Session) []uint64 {
|
||||
val, has := ses.Values[keyRoles]
|
||||
if !has {
|
||||
return nil
|
||||
@@ -36,15 +42,6 @@ func GetRoleMemberships(ses *sessions.Session) []uint64 {
|
||||
return val.([]uint64)
|
||||
}
|
||||
|
||||
// SetRoleMemberships is a session value setting wrapper for RoleMemberships
|
||||
func SetRoleMemberships(ses *sessions.Session, val []uint64) {
|
||||
if val != nil {
|
||||
ses.Values[keyRoles] = val
|
||||
} else {
|
||||
delete(ses.Values, keyRoles)
|
||||
}
|
||||
}
|
||||
|
||||
// GetOAuth2AuthParams is wrapper to get value from session
|
||||
func GetOAuth2AuthParams(ses *sessions.Session) url.Values {
|
||||
val, has := ses.Values[keyOAuth2AuthParams]
|
||||
|
||||
+63
-156
@@ -11,7 +11,6 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/errors"
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/payload"
|
||||
"github.com/cortezaproject/corteza-server/system/types"
|
||||
"github.com/go-chi/jwtauth"
|
||||
"github.com/go-oauth2/oauth2/v4"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
@@ -22,12 +21,9 @@ import (
|
||||
)
|
||||
|
||||
type (
|
||||
signer interface {
|
||||
Sign(accessToken string, identity Identifiable, clientID uint64, scope ...string) (signed []byte, err error)
|
||||
}
|
||||
|
||||
MiddlewareValidator interface {
|
||||
HttpValidator(scope ...string) func(http.Handler) http.Handler
|
||||
Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (signed []byte, err error)
|
||||
}
|
||||
|
||||
oauth2manager interface {
|
||||
@@ -48,27 +44,10 @@ type (
|
||||
|
||||
issuerClaim string
|
||||
}
|
||||
|
||||
// @todo remove
|
||||
tokenStore interface {
|
||||
CreateAuthOa2token(ctx context.Context, rr ...*types.AuthOa2token) error
|
||||
UpsertAuthConfirmedClient(ctx context.Context, rr ...*types.AuthConfirmedClient) error
|
||||
}
|
||||
|
||||
// @todo remove
|
||||
//tokenLookup interface {
|
||||
// LookupAuthOa2tokenByID(ctx context.Context, id uint64) (*types.AuthOa2token, error)
|
||||
//}
|
||||
//
|
||||
//tokenStoreWithLookup interface {
|
||||
// tokenStore
|
||||
// tokenLookup
|
||||
//}
|
||||
)
|
||||
|
||||
var (
|
||||
defaultJWTManager *jwtManager
|
||||
//DefaultJwtStore tokenStoreWithLookup
|
||||
)
|
||||
|
||||
// JWT returns d
|
||||
@@ -105,31 +84,13 @@ func NewJWTManager(oa2m oauth2manager, algo jwa.SignatureAlgorithm, secret strin
|
||||
return
|
||||
}
|
||||
|
||||
//// @todo remove
|
||||
////// SetJWTStore set store for JWT
|
||||
////// @todo find better way to initiate store,
|
||||
////// it mainly used for generating and storing accessToken for impersonate and corredor, Ref: j.Generate()
|
||||
////func SetJWTStore(store tokenStoreWithLookup) {
|
||||
//// DefaultJwtStore = store
|
||||
////}
|
||||
//
|
||||
//// Authenticate the token from the given string and return parsed token or error
|
||||
//func (m *jwtManager) Authenticate(s string) (pToken jwt.Token, err error) {
|
||||
// if pToken, err = jwt.Parse([]byte(s), jwt.WithVerify(m.signAlgo, m.signKey)); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if err = jwt.Validate(pToken); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// return
|
||||
//}
|
||||
|
||||
// Sign takes security information and returns signed JWT
|
||||
//
|
||||
// Access token is expected to be issued by OAuth2 token manager
|
||||
// without it, we can only do static (JWT itself) validation
|
||||
// Access token is expected to be issued by OAuth2 token manager and we want to
|
||||
// transport access-token one of the JWT claims (JWT ID!).
|
||||
//
|
||||
// This way we can perform static checks (origin, validity, expiration)
|
||||
// before doing any storage lookups.
|
||||
//f
|
||||
// Identity holds user ID and all roles that go into this security context
|
||||
// Client ID represents the auth client that was used
|
||||
@@ -190,6 +151,13 @@ func (m *jwtManager) Sign(accessToken string, identity Identifiable, clientID ui
|
||||
return signed, nil
|
||||
}
|
||||
|
||||
// Generate new access-token and JWT
|
||||
//
|
||||
// Why so much effort and not just return the access token?
|
||||
// We want to transport access-token one of the JWT claims (JWT ID!).
|
||||
//
|
||||
// This way we can perform static checks (origin, validity, expiration)
|
||||
// before doing any storage lookups.
|
||||
func (m *jwtManager) Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (signed []byte, err error) {
|
||||
var (
|
||||
ti oauth2.TokenInfo
|
||||
@@ -199,7 +167,7 @@ func (m *jwtManager) Generate(ctx context.Context, i Identifiable, clientID uint
|
||||
ClientID: strconv.FormatUint(clientID, 10),
|
||||
UserID: i.String(),
|
||||
Scope: strings.Join(scope, " "),
|
||||
Refresh: "cli?",
|
||||
Refresh: "??????????",
|
||||
AccessTokenExp: m.expiry,
|
||||
})
|
||||
|
||||
@@ -207,42 +175,7 @@ func (m *jwtManager) Generate(ctx context.Context, i Identifiable, clientID uint
|
||||
return
|
||||
}
|
||||
|
||||
return m.Sign(ti.GetAccess(), i, 0, scope...)
|
||||
}
|
||||
|
||||
func ValidateContext(ctx context.Context, oa2m oauth2manager, scope ...string) (err error) {
|
||||
var (
|
||||
token jwt.Token
|
||||
)
|
||||
|
||||
if token, _, err = jwtauth.FromContext(ctx); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return Validate(ctx, token, oa2m, scope...)
|
||||
}
|
||||
|
||||
func Validate(ctx context.Context, token jwt.Token, oa2m oauth2manager, scope ...string) (err error) {
|
||||
if !CheckJwtScope(token, scope...) {
|
||||
return ErrUnauthorizedScope()
|
||||
}
|
||||
|
||||
// Extract the JWT id from the token (string) and convert it to uint64
|
||||
// to be compatible with the lookup function
|
||||
if len(token.JwtID()) < 10 {
|
||||
return ErrMalformedToken("missing or malformed JWT ID")
|
||||
}
|
||||
|
||||
// @todo we could use a simple caching mechanism here
|
||||
// 1. if lookup is successful, add a JWT ID to the list
|
||||
// 2. add short exp time (that should not last longer than token's exp time)
|
||||
// 3. check against the list first; if JWT ID is not present there check in storage
|
||||
//
|
||||
if _, err = oa2m.LoadAccessToken(ctx, token.JwtID()); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return nil
|
||||
return m.Sign(ti.GetAccess(), i, clientID, scope...)
|
||||
}
|
||||
|
||||
// HttpVerifier http middleware handler will verify a JWT string from a http request.
|
||||
@@ -271,85 +204,59 @@ func (m *jwtManager) HttpValidator(scope ...string) func(http.Handler) http.Hand
|
||||
}
|
||||
}
|
||||
|
||||
//// HttpAuthenticator converts JWT claims into identity and stores it into context
|
||||
//func (m *jwtManager) HttpAuthenticator(next http.Handler) http.Handler {
|
||||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
// ctx := r.Context()
|
||||
//
|
||||
// tkn, _, err := jwtauth.FromContext(ctx)
|
||||
//
|
||||
// // Requests w/o token should not yield an error
|
||||
// // there are parts of the system that can be access without it
|
||||
// // and/or handle such situation internally
|
||||
// if err != nil && !errors.Is(err, jwtauth.ErrNoTokenFound) {
|
||||
// api.Send(w, r, err)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // If token is present extract identity
|
||||
// if tkn != nil {
|
||||
// ctx = SetIdentityToContext(ctx, IdentityFromToken(tkn))
|
||||
// r = r.WithContext(ctx)
|
||||
//
|
||||
// // @todo verify JWT ID (access-token!!
|
||||
// tkn.JwtID()
|
||||
//
|
||||
// }
|
||||
//
|
||||
// next.ServeHTTP(w, r)
|
||||
// })
|
||||
//}
|
||||
func (m *jwtManager) ValidateContext(ctx context.Context, scope ...string) error {
|
||||
return ValidateContext(ctx, m.oa2m, scope...)
|
||||
}
|
||||
|
||||
func (m *jwtManager) Validate(ctx context.Context, token jwt.Token, scope ...string) error {
|
||||
return Validate(ctx, token, m.oa2m, scope...)
|
||||
}
|
||||
|
||||
// ValidateContext gets JWT & claims from context
|
||||
//
|
||||
//// Generate makes a new token and stores it in the database
|
||||
//func (tm *tokenManager) Generate(ctx context.Context, i Identifiable, clientID uint64, scope ...string) (token []byte, err error) {
|
||||
// var (
|
||||
// // eti = GetExtraReqInfoFromContext(ctx)
|
||||
// // oa2t = &types.AuthOa2token{
|
||||
// // ID: id.Next(),
|
||||
// // CreatedAt: time.Now().Round(time.Second),
|
||||
// // RemoteAddr: eti.RemoteAddr,
|
||||
// // UserAgent: eti.UserAgent,
|
||||
// // ClientID: clientID,
|
||||
// // }
|
||||
// //
|
||||
// // acc = &types.AuthConfirmedClient{
|
||||
// // ConfirmedAt: oa2t.CreatedAt,
|
||||
// // ClientID: clientID,
|
||||
// // }
|
||||
// oa2t *types.AuthOa2token
|
||||
// acc *types.AuthConfirmedClient
|
||||
// It's chi middleware that puts it there
|
||||
func ValidateContext(ctx context.Context, oa2m oauth2manager, scope ...string) (err error) {
|
||||
var (
|
||||
token jwt.Token
|
||||
)
|
||||
|
||||
if token, _, err = jwtauth.FromContext(ctx); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return Validate(ctx, token, oa2m, scope...)
|
||||
}
|
||||
|
||||
// Validate performs token validation
|
||||
//
|
||||
// jwtID = id.Next()
|
||||
// )
|
||||
// Steps:
|
||||
// - check scope in the JWT
|
||||
// - check if JWT ID is set (where the access-token string is stored)
|
||||
// - check if access-token exists in the DB
|
||||
//
|
||||
// if oa2t, acc, err = MakeAuthStructs(ctx, jwtID, i.Identity(), clientID, nil, tm.expiry); err != nil {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if token, err = tm.make(jwtID, i, clientID, scope...); err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// oa2t.Access = string(token)
|
||||
//
|
||||
// // use the same expiration as on token
|
||||
// //oa2t.ExpiresAt = oa2t.CreatedAt.Add(tm.expiry)
|
||||
//
|
||||
// //if oa2t.Data, err = json.Marshal(oa2t); err != nil {
|
||||
// // return
|
||||
// //}
|
||||
//
|
||||
// //if oa2t.UserID, _ = ExtractFromSubClaim(i.String()); oa2t.UserID == 0 {
|
||||
// // // UserID stores collection of IDs: user's ID and set of all roles' user is member of
|
||||
// // return nil, fmt.Errorf("could not parse user ID from token")
|
||||
// //}
|
||||
// //
|
||||
// //// copy user id to auth client confirmation
|
||||
// //acc.UserID = oa2t.UserID
|
||||
//
|
||||
// return token, StoreAuthToken(ctx, DefaultJwtStore, oa2t, acc)
|
||||
//}
|
||||
func Validate(ctx context.Context, token jwt.Token, oa2m oauth2manager, scope ...string) (err error) {
|
||||
if len(scope) > 0 && !CheckJwtScope(token, scope...) {
|
||||
return ErrUnauthorizedScope()
|
||||
}
|
||||
|
||||
// Extract the JWT id from the token (string) and convert it to uint64
|
||||
// to be compatible with the lookup function
|
||||
if len(token.JwtID()) < 10 {
|
||||
return ErrMalformedToken("missing or malformed JWT ID")
|
||||
}
|
||||
|
||||
// @todo we could use a simple caching mechanism here
|
||||
// 1. if lookup is successful, add a JWT ID to the list
|
||||
// 2. add short exp time (that should not last longer than token's exp time)
|
||||
// 3. check against the list first; if JWT ID is not present there check in storage
|
||||
//
|
||||
if _, err = oa2m.LoadAccessToken(ctx, token.JwtID()); err != nil {
|
||||
return ErrUnauthorized()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IdentityFromToken decodes sub & roles claims into identity
|
||||
func IdentityFromToken(token jwt.Token) *identity {
|
||||
|
||||
@@ -104,7 +104,7 @@ type (
|
||||
}
|
||||
|
||||
authTokenMaker interface {
|
||||
Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (token []byte, err error)
|
||||
Generate(ctx context.Context, i auth.Identifiable, clientID uint64, scope ...string) (signed []byte, err error)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -735,7 +735,7 @@ func (svc service) exec(ctx context.Context, script string, runAs string, args S
|
||||
}
|
||||
|
||||
// Generate and save the token
|
||||
token, err = svc.authTokenMaker.Generate(ctx, definer, 0)
|
||||
token, err = svc.authTokenMaker.Generate(ctx, definer, 0, "profile", "api")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ var (
|
||||
)
|
||||
|
||||
type (
|
||||
jwtValidator interface {
|
||||
Validate(ctx context.Context, token jwt.Token, scope ...string) error
|
||||
}
|
||||
|
||||
session struct {
|
||||
id uint64
|
||||
once sync.Once
|
||||
@@ -45,6 +49,8 @@ type (
|
||||
|
||||
identity auth.Identifiable
|
||||
|
||||
jv jwtValidator
|
||||
|
||||
server *server
|
||||
}
|
||||
)
|
||||
@@ -56,6 +62,7 @@ func Session(ctx context.Context, ws *server, conn *websocket.Conn) *session {
|
||||
config: ws.config,
|
||||
send: make(chan []byte, 512),
|
||||
stop: make(chan []byte, 1),
|
||||
jv: auth.JWT(),
|
||||
server: ws,
|
||||
}
|
||||
|
||||
@@ -292,14 +299,10 @@ func (s *session) authenticate(p *payloadAuth) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = jwt.Validate(token); err != nil {
|
||||
if err = s.jv.Validate(s.ctx, token, "api"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !auth.CheckJwtScope(token, "api") {
|
||||
return fmt.Errorf("client does not allow use of websockets (missing 'api' scope)")
|
||||
}
|
||||
|
||||
// Get identity using JWT claims
|
||||
identity := auth.IdentityFromToken(token)
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -8,14 +9,26 @@ import (
|
||||
"github.com/cortezaproject/corteza-server/pkg/logger"
|
||||
"github.com/cortezaproject/corteza-server/pkg/options"
|
||||
"github.com/lestrrat-go/jwx/jwa"
|
||||
"github.com/lestrrat-go/jwx/jwt"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type (
|
||||
dummyJwtValidator struct{ err error }
|
||||
)
|
||||
|
||||
func (d *dummyJwtValidator) Validate(_ context.Context, _ jwt.Token, _ ...string) error {
|
||||
return d.err
|
||||
}
|
||||
|
||||
func TestSession_procRawMessage(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
s = session{server: Server(nil, options.WebsocketOpt{})}
|
||||
s = session{
|
||||
server: Server(nil, options.WebsocketOpt{}),
|
||||
jv: &dummyJwtValidator{},
|
||||
}
|
||||
|
||||
userID uint64 = 123
|
||||
token []byte
|
||||
@@ -68,6 +81,4 @@ func TestSession_procRawMessage(t *testing.T) {
|
||||
req.NoError(err)
|
||||
|
||||
req.EqualError(s.procRawMessage(mockResponse(token)), "unauthorized: identity does not match")
|
||||
|
||||
t.Error("are we actually checking if access token exists?")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user