fix: persist login sessions for workbench auth

This commit is contained in:
richjohnrambo
2026-09-15 10:23:47 +08:00
parent ef8091d805
commit 9ec5da5b02
3 changed files with 55 additions and 1 deletions
+16 -1
View File
@@ -1317,7 +1317,22 @@ func (s *Session) onLogin(msgID string, timestamp time.Time, rec *auth.Rec, miss
// GenSecret fails only if tokenLifetime is < 0. It can't be < 0 here, // GenSecret fails only if tokenLifetime is < 0. It can't be < 0 here,
// otherwise login would have failed earlier. // otherwise login would have failed earlier.
rec.Features = features rec.Features = features
params["token"], params["expires"], _ = store.Store.GetLogicalAuthHandler("token").GenSecret(rec) token, expires, _ := store.Store.GetLogicalAuthHandler("token").GenSecret(rec)
params["token"], params["expires"] = token, expires
if !s.uid.IsZero() && features&auth.FeatureNoLogin == 0 {
if enabled, err := store.StoreLoginSession(token, store.LoginSession{
UserID: store.DecodeUid(rec.Uid),
TenantID: int64(s.tenantID),
TenantCode: s.tenantCode,
DeviceID: s.deviceID,
AuthLevel: rec.AuthLevel.String(),
State: "active",
ExpiresAt: expires.Format(time.RFC3339),
IssuedAt: time.Now().UTC().Format(time.RFC3339),
}, time.Until(expires)); enabled && err != nil {
logs.Warn.Println("s.onLogin: failed to store login session", rec.Uid, err, s.sid)
}
}
reply.Ctrl.Params = params reply.Ctrl.Params = params
return reply return reply
+35
View File
@@ -62,6 +62,17 @@ type RealtimeFanoutEnvelope struct {
Message json.RawMessage `json:"message"` Message json.RawMessage `json:"message"`
} }
type LoginSession struct {
UserID int64 `json:"user_id"`
TenantID int64 `json:"tenant_id"`
TenantCode string `json:"tenant_code"`
DeviceID string `json:"device_id,omitempty"`
AuthLevel string `json:"auth_level"`
State string `json:"state"`
ExpiresAt string `json:"expires_at"`
IssuedAt string `json:"issued_at"`
}
type groupMessageRuntime interface { type groupMessageRuntime interface {
Close() error Close() error
Claim(tenantID types.TenantID, topic string, from types.Uid, clientMsgID string) (*groupMessageUniqueRecord, bool, error) Claim(tenantID types.TenantID, topic string, from types.Uid, clientMsgID string) (*groupMessageUniqueRecord, bool, error)
@@ -95,6 +106,7 @@ type groupMessageRuntime interface {
ClaimShortState(namespace, id string, ttl time.Duration) (bool, error) ClaimShortState(namespace, id string, ttl time.Duration) (bool, error)
ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, error) ClaimShortStateValue(namespace, id, value string, ttl time.Duration) (bool, bool, error)
ShortStateExists(namespace, id string) (bool, error) ShortStateExists(namespace, id string) (bool, error)
StoreLoginSession(token []byte, session LoginSession, ttl time.Duration) error
} }
var groupMsgRuntime groupMessageRuntime var groupMsgRuntime groupMessageRuntime
@@ -190,6 +202,13 @@ func IsLoginTokenRevoked(tenantID types.TenantID, token []byte) (bool, bool, err
return revoked, true, err return revoked, true, err
} }
func StoreLoginSession(token []byte, session LoginSession, ttl time.Duration) (bool, error) {
if groupMsgRuntime == nil || len(token) == 0 || ttl <= 0 {
return false, nil
}
return true, groupMsgRuntime.StoreLoginSession(token, session, ttl)
}
func RefreshUserPresence(tenantID types.TenantID, uid types.Uid, sid, nodeID string) error { func RefreshUserPresence(tenantID types.TenantID, uid types.Uid, sid, nodeID string) error {
if groupMsgRuntime == nil || !tenantID.IsValid() || uid.IsZero() || sid == "" { if groupMsgRuntime == nil || !tenantID.IsValid() || uid.IsZero() || sid == "" {
return nil return nil
@@ -990,6 +1009,18 @@ func (r *redisGroupMessageRuntime) ShortStateExists(namespace, id string) (bool,
return count > 0, err return count > 0, err
} }
func (r *redisGroupMessageRuntime) StoreLoginSession(token []byte, session LoginSession, ttl time.Duration) error {
if len(token) == 0 || ttl <= 0 {
return nil
}
digest := sha256.Sum256(token)
payload, err := json.Marshal(session)
if err != nil {
return err
}
return r.client.Set(context.Background(), r.loginSessionKey(hex.EncodeToString(digest[:])), payload, ttl).Err()
}
func (r *redisGroupMessageRuntime) hotSeqIDs(tenantID types.TenantID, topic string, opts *types.QueryOpt, limit int) ([]int64, bool, error) { func (r *redisGroupMessageRuntime) hotSeqIDs(tenantID types.TenantID, topic string, opts *types.QueryOpt, limit int) ([]int64, bool, error) {
if opts != nil && len(opts.IdRanges) > 0 { if opts != nil && len(opts.IdRanges) > 0 {
seqs := expandRanges(opts.IdRanges, limit) seqs := expandRanges(opts.IdRanges, limit)
@@ -1133,6 +1164,10 @@ func (r *redisGroupMessageRuntime) shortStateKey(namespace, id string) string {
return r.key(types.ZeroTenantID, "short", namespace, id) return r.key(types.ZeroTenantID, "short", namespace, id)
} }
func (r *redisGroupMessageRuntime) loginSessionKey(tokenHash string) string {
return strings.TrimSuffix(r.prefix, ":") + ":session:token:" + tokenHash
}
func (r *redisGroupMessageRuntime) cacheTTL(namespace string) time.Duration { func (r *redisGroupMessageRuntime) cacheTTL(namespace string) time.Duration {
ttl := r.cfg.CacheTTL ttl := r.cfg.CacheTTL
if namespace == "verification" || namespace == "action_token" { if namespace == "verification" || namespace == "action_token" {
@@ -171,6 +171,10 @@ func (r *testGroupMessageRuntime) ShortStateExists(namespace, id string) (bool,
return r.shortStates[namespace+":"+id], nil return r.shortStates[namespace+":"+id], nil
} }
func (r *testGroupMessageRuntime) StoreLoginSession([]byte, LoginSession, time.Duration) error {
return nil
}
func TestLoginTokenRevocation(t *testing.T) { func TestLoginTokenRevocation(t *testing.T) {
oldRuntime := groupMsgRuntime oldRuntime := groupMsgRuntime
groupMsgRuntime = newTestGroupMessageRuntime() groupMsgRuntime = newTestGroupMessageRuntime()