GitHub Security Advisory: GHSA-258c-965c-p3hc
Package: github.com/daptin/daptin (GO)
Severity: MODERATE (CVSS 6.5)
CWE: CWE-613: Insufficient Session Expiration
Vulnerable versions: < 0.11.8
Patched in: 0.11.8

Summary: Daptin's Session Management Vulnerability Leads to Insufficient Session Expiration After Password Change

Details:
### Summary

A session invalidation vulnerability exists in daptin's authentication system where JSON Web Tokens (JWTs) remain fully valid after a user changes their password. The JWT validation middleware (`CheckJWT`) only verifies token signature, expiry, issuer, and signing algorithm — it does not check whether the token was issued before the most recent password change. The password update code path hashes the new password but never calls `InvalidateAuthCacheForEmail()` and never revokes or blacklists existing tokens. This effectively negating password rotation as an incident response control.

#### Vulnerable Files

* `daptin/server/jwt/jwtmiddleware.go` — JWT validation without session versioning
* `daptin/server/resource/resource_update.go` — password update without session invalidation
* `daptin/server/actions/action_generate_jwt_token.go` — JWT claims lack password version
* `daptin/server/auth/auth.go` — `InvalidateAuthCacheForEmail` exists but not called on update
* `daptin/server/resource/columns.go` — password change action wiring

#### Vulnerable Code Snippet

**1. JWT validation checks nothing beyond signature/expiry/issuer (jwtmiddleware.go:232-260):**

```go
// Now parse the token
parsedToken, err := jwt.Parse(token, m.Options.ValidationKeyGetter)

// Check if there was an error in parsing...
if err != nil {
    m.logf("Error parsing token: %v", err)
    m.Options.ErrorHandler(w, r, err.Error())
    return nil, fmt.Errorf("Error parsing token: %v", err)
}

if parsedToken.Claims.(jwt.MapClaims)["iss"] != m.Options.Issuer {
    return nil, fmt.Errorf("Invalid issuer: %v", parsedToken.Header["iss"])
}

if m.Options.SigningMethod != nil && m.Options.SigningMethod.Alg() != parsedToken.Header["alg"] {
    // ... algorithm check
}

// Check if the parsed token is valid...
if !parsedToken.Valid {
    m.logf("Token is invalid")
    m.Options.ErrorHandler(w, r, "The token isn't valid")
    return nil, errors.New("Token is invalid")
}
```

**No check exists for password version, session version, or token revocation status.** A token issued before a password change passes all these checks identically.

**2. Password update hashes new password but never invalidates sessions (resource_update.go:282-287):**

```go
if col.ColumnType == "password" {
    val, err = BcryptHashString(val.(string))
    if err != nil {
        log.Errorf("Failed to convert string to bcrypt hash, not storing the value: %v", err)
        continue
    }
}
```

The new bcrypt hash is stored, but no call to `auth.InvalidateAuthCacheForEmail()` is made, and no token revocation mechanism is triggered.

**3. JWT claims lack any password-bound claim (action_generate_jwt_token.go:74-83):**

```go
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
    "email": existingUser["email"],
    "sub":   daptinid.InterfaceToDIR(existingUser["reference_id"]).String(),
    "name":  existingUser["name"],
    "nbf":   timeNow.Unix(),
    "exp":   timeNow.Add(time.Duration(d.to