Fix: Propagate OIDC claims for dynamic IAM policies (#8060)
Fix: Propagate OIDC claims to IAM identity for dynamic policy variables Fixes #8037. Ensures additional OIDC claims (like preferred_username) are preserved in ExternalIdentity attributes and propagated to IAM tokens, enabling substitution in dynamic policies.
This commit is contained in:
@@ -237,6 +237,34 @@ func (p *OIDCProvider) Authenticate(ctx context.Context, token string) (*provide
|
||||
attributes["roles"] = strings.Join(roles, ",")
|
||||
}
|
||||
|
||||
// Store all additional claims as attributes
|
||||
processedClaims := map[string]struct{}{
|
||||
// user / business claims already handled elsewhere
|
||||
"sub": {},
|
||||
"email": {},
|
||||
"name": {},
|
||||
"groups": {},
|
||||
"roles": {},
|
||||
// standard structural OIDC/JWT claims that should not be exposed as attributes
|
||||
"iss": {},
|
||||
"aud": {},
|
||||
"exp": {},
|
||||
"iat": {},
|
||||
"nbf": {},
|
||||
"jti": {},
|
||||
}
|
||||
for key, value := range claims.Claims {
|
||||
if _, isProcessed := processedClaims[key]; !isProcessed {
|
||||
if strValue, ok := value.(string); ok {
|
||||
attributes[key] = strValue
|
||||
} else if jsonValue, err := json.Marshal(value); err == nil {
|
||||
attributes[key] = string(jsonValue)
|
||||
} else {
|
||||
glog.Warningf("failed to marshal claim %q to JSON for OIDC attributes: %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
identity := &providers.ExternalIdentity{
|
||||
UserID: claims.Subject,
|
||||
Email: email,
|
||||
|
||||
@@ -248,6 +248,60 @@ func TestOIDCProviderAuthentication(t *testing.T) {
|
||||
assert.Contains(t, identity.Groups, "developers")
|
||||
})
|
||||
|
||||
t.Run("successful authentication with additional attributes", func(t *testing.T) {
|
||||
token := createTestJWT(t, privateKey, jwt.MapClaims{
|
||||
"iss": server.URL,
|
||||
"aud": "test-client",
|
||||
"sub": "user123",
|
||||
"exp": time.Now().Add(time.Hour).Unix(),
|
||||
"iat": time.Now().Unix(),
|
||||
"email": "user@example.com",
|
||||
"name": "Test User",
|
||||
"groups": []string{"users"},
|
||||
"preferred_username": "myusername", // Extra claim
|
||||
"department": "engineering", // Extra claim
|
||||
"custom_number": 42, // Non-string claim
|
||||
"custom_avg": 98.6, // Non-string claim
|
||||
"custom_object": map[string]interface{}{"nested": "value"}, // Nested object claim
|
||||
})
|
||||
|
||||
identity, err := provider.Authenticate(context.Background(), token)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, identity)
|
||||
|
||||
// Check standard fields
|
||||
assert.Equal(t, "user123", identity.UserID)
|
||||
|
||||
// Check attributes
|
||||
val, exists := identity.Attributes["preferred_username"]
|
||||
assert.True(t, exists, "preferred_username should be in attributes")
|
||||
assert.Equal(t, "myusername", val)
|
||||
|
||||
val, exists = identity.Attributes["department"]
|
||||
assert.True(t, exists, "department should be in attributes")
|
||||
assert.Equal(t, "engineering", val)
|
||||
|
||||
// Test non-string claims (should be JSON marshaled)
|
||||
val, exists = identity.Attributes["custom_number"]
|
||||
assert.True(t, exists, "custom_number should be in attributes")
|
||||
assert.Equal(t, "42", val)
|
||||
|
||||
val, exists = identity.Attributes["custom_avg"]
|
||||
assert.True(t, exists, "custom_avg should be in attributes")
|
||||
assert.Contains(t, val, "98.6") // JSON number formatting might vary
|
||||
|
||||
val, exists = identity.Attributes["custom_object"]
|
||||
assert.True(t, exists, "custom_object should be in attributes")
|
||||
assert.Contains(t, val, "\"nested\":\"value\"")
|
||||
|
||||
// Verify structural JWT claims are excluded from attributes
|
||||
excludedClaims := []string{"iss", "aud", "exp", "iat"}
|
||||
for _, claim := range excludedClaims {
|
||||
_, exists := identity.Attributes[claim]
|
||||
assert.False(t, exists, "standard claim %s should not be in attributes", claim)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("authentication with invalid token", func(t *testing.T) {
|
||||
_, err := provider.Authenticate(context.Background(), "invalid-token")
|
||||
assert.Error(t, err)
|
||||
|
||||
Reference in New Issue
Block a user