Browse Source

fix: propagate OIDC attributes to STS session token for IAM policies (#8079)

* fix: propagate OIDC attributes to STS session token

* refactor: apply PR suggestions for STS session claims
pull/7934/merge
Chris Lu 2 days ago
committed by GitHub
parent
commit
cd2e93bf2b
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 23
      weed/iam/sts/sts_service.go
  2. 93
      weed/iam/sts/sts_service_test.go

23
weed/iam/sts/sts_service.go

@ -458,12 +458,33 @@ func (s *STSService) AssumeRoleWithWebIdentity(ctx context.Context, request *Ass
Subject: externalIdentity.UserID,
}
// Create request context from identity attributes for policy evaluation
requestContext := make(map[string]interface{}, len(externalIdentity.Attributes)+3)
// Add generic attributes (including preferred_username, etc.)
if externalIdentity.Attributes != nil {
for k, v := range externalIdentity.Attributes {
requestContext[k] = v
}
}
// Add standard OIDC fields if not already present
if _, ok := requestContext["email"]; !ok && externalIdentity.Email != "" {
requestContext["email"] = externalIdentity.Email
}
if _, ok := requestContext["name"]; !ok && externalIdentity.DisplayName != "" {
requestContext["name"] = externalIdentity.DisplayName
}
// Add sub as well since it's commonly used
requestContext["sub"] = externalIdentity.UserID
// Create rich JWT claims with all session information
sessionClaims := NewSTSSessionClaims(sessionId, s.Config.Issuer, expiresAt).
WithSessionName(request.RoleSessionName).
WithRoleInfo(request.RoleArn, assumedRoleUser.Arn, assumedRoleUser.Arn).
WithIdentityProvider(provider.Name(), externalIdentity.UserID, "").
WithMaxDuration(sessionDuration)
WithMaxDuration(sessionDuration).
WithRequestContext(requestContext)
// Generate self-contained JWT token with all session information
jwtToken, err := s.tokenGenerator.GenerateJWTWithClaims(sessionClaims)

93
weed/iam/sts/sts_service_test.go

@ -660,3 +660,96 @@ func (m *MockIdentityProviderWithExpiration) ValidateToken(ctx context.Context,
func timePtr(t time.Time) *time.Time {
return &t
}
// TestAssumeRoleWithWebIdentity_PreservesAttributes tests that attributes from the identity provider
// are correctly propagated to the session token's request context
func TestAssumeRoleWithWebIdentity_PreservesAttributes(t *testing.T) {
service := setupTestSTSService(t)
// Create a mock provider that returns a user with attributes
mockProvider := &MockIdentityProviderWithAttributes{
name: "attr-provider",
attributes: map[string]string{
"preferred_username": "my-user",
"department": "engineering",
"project": "seaweedfs",
},
}
service.RegisterProvider(mockProvider)
// Create a valid JWT token for the provider
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"iss": "attr-provider",
"sub": "test-user-id",
"aud": "test-client",
"exp": time.Now().Add(time.Hour).Unix(),
"iat": time.Now().Unix(),
})
tokenString, err := token.SignedString([]byte("test-signing-key"))
require.NoError(t, err)
ctx := context.Background()
request := &AssumeRoleWithWebIdentityRequest{
RoleArn: "arn:aws:iam::role/TestRole",
WebIdentityToken: tokenString,
RoleSessionName: "test-session",
}
response, err := service.AssumeRoleWithWebIdentity(ctx, request)
require.NoError(t, err)
require.NotNil(t, response)
// Validate the session token to check claims
sessionInfo, err := service.ValidateSessionToken(ctx, response.Credentials.SessionToken)
require.NoError(t, err)
// Check that attributes are present in RequestContext
require.NotNil(t, sessionInfo.RequestContext, "RequestContext should not be nil")
assert.Equal(t, "my-user", sessionInfo.RequestContext["preferred_username"])
assert.Equal(t, "engineering", sessionInfo.RequestContext["department"])
assert.Equal(t, "seaweedfs", sessionInfo.RequestContext["project"])
// Check standard claims are also present
assert.Equal(t, "test-user-id", sessionInfo.RequestContext["sub"])
assert.Equal(t, "test@example.com", sessionInfo.RequestContext["email"])
assert.Equal(t, "Test User", sessionInfo.RequestContext["name"])
}
// MockIdentityProviderWithAttributes is a mock provider that returns configured attributes
type MockIdentityProviderWithAttributes struct {
name string
attributes map[string]string
}
func (m *MockIdentityProviderWithAttributes) Name() string {
return m.name
}
func (m *MockIdentityProviderWithAttributes) GetIssuer() string {
return m.name
}
func (m *MockIdentityProviderWithAttributes) Initialize(config interface{}) error {
return nil
}
func (m *MockIdentityProviderWithAttributes) Authenticate(ctx context.Context, token string) (*providers.ExternalIdentity, error) {
return &providers.ExternalIdentity{
UserID: "test-user-id",
Email: "test@example.com",
DisplayName: "Test User",
Provider: m.name,
Attributes: m.attributes,
}, nil
}
func (m *MockIdentityProviderWithAttributes) GetUserInfo(ctx context.Context, userID string) (*providers.ExternalIdentity, error) {
return nil, nil
}
func (m *MockIdentityProviderWithAttributes) ValidateToken(ctx context.Context, token string) (*providers.TokenClaims, error) {
return &providers.TokenClaims{
Subject: "test-user-id",
Issuer: m.name,
}, nil
}
Loading…
Cancel
Save