diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-12-08 17:38:35 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-12-08 17:38:35 -0800 |
| commit | ff4855dcbe784eefa34e5f3298ebc071e10ed208 (patch) | |
| tree | 27fcc37d2e3b37c68146a02dc92cf623f14c27da /weed/iam/oidc/oidc_provider.go | |
| parent | 772459f93ca5d77160c4b827a781a53ef91cc31c (diff) | |
| download | seaweedfs-ff4855dcbe784eefa34e5f3298ebc071e10ed208.tar.xz seaweedfs-ff4855dcbe784eefa34e5f3298ebc071e10ed208.zip | |
sts: limit session duration to incoming token's exp claim (#7670)
* sts: limit session duration to incoming token's exp claim
This fixes the issue where AssumeRoleWithWebIdentity would issue sessions
that outlive the source identity token's expiration.
For use cases like GitLab CI Jobs where the ID Token has an exp claim
limited to the CI job's timeout, the STS session should not exceed that
expiration.
Changes:
- Add TokenExpiration field to ExternalIdentity struct
- Extract exp/iat/nbf claims in OIDC provider's ValidateToken
- Pass token expiration from Authenticate to ExternalIdentity
- Modify calculateSessionDuration to cap at source token's exp
- Add comprehensive tests for the new behavior
Fixes: https://github.com/seaweedfs/seaweedfs/discussions/7653
* refactor: reduce duplication in time claim extraction
Use a loop over claim names instead of repeating the same
extraction logic three times for exp, iat, and nbf claims.
* address review: add defense-in-depth for expired tokens
- Handle already-expired tokens defensively with 1 minute minimum duration
- Enforce MaxSessionLength from config as additional cap
- Fix potential nil dereference in test mock
- Add test case for expired token scenario
* remove issue reference from test
* fix: remove early return to ensure MaxSessionLength is always checked
Diffstat (limited to 'weed/iam/oidc/oidc_provider.go')
| -rw-r--r-- | weed/iam/oidc/oidc_provider.go | 30 |
1 files changed, 28 insertions, 2 deletions
diff --git a/weed/iam/oidc/oidc_provider.go b/weed/iam/oidc/oidc_provider.go index d31f322b0..fe1cdaccb 100644 --- a/weed/iam/oidc/oidc_provider.go +++ b/weed/iam/oidc/oidc_provider.go @@ -186,14 +186,22 @@ func (p *OIDCProvider) Authenticate(ctx context.Context, token string) (*provide attributes["roles"] = strings.Join(roles, ",") } - return &providers.ExternalIdentity{ + identity := &providers.ExternalIdentity{ UserID: claims.Subject, Email: email, DisplayName: displayName, Groups: groups, Attributes: attributes, Provider: p.name, - }, nil + } + + // Pass the token expiration to limit session duration + // This ensures the STS session doesn't exceed the source token's validity + if !claims.ExpiresAt.IsZero() { + identity.TokenExpiration = &claims.ExpiresAt + } + + return identity, nil } // GetUserInfo retrieves user information from the UserInfo endpoint @@ -372,6 +380,24 @@ func (p *OIDCProvider) ValidateToken(ctx context.Context, token string) (*provid Claims: make(map[string]interface{}), } + // Extract time-based claims (exp, iat, nbf) + for key, target := range map[string]*time.Time{ + "exp": &tokenClaims.ExpiresAt, + "iat": &tokenClaims.IssuedAt, + "nbf": &tokenClaims.NotBefore, + } { + if val, ok := claims[key]; ok { + switch v := val.(type) { + case float64: + *target = time.Unix(int64(v), 0) + case json.Number: + if intVal, err := v.Int64(); err == nil { + *target = time.Unix(intVal, 0) + } + } + } + } + // Copy all claims for key, value := range claims { tokenClaims.Claims[key] = value |
