feat: add spaceDelimitedClaims field to RequestAuthentication API (#3547)

Signed-off-by: Francisco Herrera <fjglira@gmail.com>
This commit is contained in:
Francisco Herrera 2025-09-23 05:44:28 +02:00 committed by GitHub
parent 4f6a6c5edb
commit 87475465cd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 178 additions and 6 deletions

View File

@ -16360,6 +16360,14 @@ spec:
description: This field specifies the header name to output
a successfully verified JWT payload to the backend.
type: string
spaceDelimitedClaims:
description: List of JWT claim names that should be treated
as space-delimited strings.
items:
minLength: 1
type: string
maxItems: 64
type: array
timeout:
description: The maximum amount of time that the resolver, determined
by the PILOT_JWT_ENABLE_REMOTE_JWKS environment variable,
@ -16641,6 +16649,14 @@ spec:
description: This field specifies the header name to output
a successfully verified JWT payload to the backend.
type: string
spaceDelimitedClaims:
description: List of JWT claim names that should be treated
as space-delimited strings.
items:
minLength: 1
type: string
maxItems: 64
type: array
timeout:
description: The maximum amount of time that the resolver, determined
by the PILOT_JWT_ENABLE_REMOTE_JWKS environment variable,

View File

@ -0,0 +1,13 @@
apiVersion: release-notes/v2
kind: feature
area: security
issue:
- https://github.com/istio/istio/issues/56873
releaseNotes:
- |
**Added** `spaceDelimitedClaims` field in `RequestAuthentication` under `spec.jwtRules.` to configure custom JWT claims
that should be treated as space-delimited strings.
This allows authorization policies to match individual values within space-separated claim strings,
extending beyond the default `scope` and `permission` claims.
This addresses compatibility issues when upgrading from older Istio versions with custom space-delimited JWT claim fields.

View File

@ -53,6 +53,21 @@ type RequestAuthentication = v1beta1.RequestAuthentication
// fromHeaders:
// - "x-goog-iap-jwt-assertion"
// ```
//
// This example shows how to configure custom claims to be treated as space-delimited strings.
// This is useful when JWT tokens contain custom claims with multiple space-separated values
// that should be available for individual matching in authorization policies.
//
// ```yaml
// issuer: https://example.com
// spaceDelimitedClaims:
// - "custom_scope"
// - "provider.login.scope"
// - "roles"
// ```
//
// With this configuration, a JWT containing `"custom_scope": "read write admin"` will allow
// authorization policies to match against individual values like "read", "write", or "admin".
// +kubebuilder:validation:XValidation:message="only one of jwks or jwksUri can be set",rule="oneof(self.jwksUri, self.jwks_uri, self.jwks)"
type JWTRule = v1beta1.JWTRule

View File

@ -395,6 +395,21 @@ func (x *RequestAuthentication) GetJwtRules() []*JWTRule {
// fromHeaders:
// - "x-goog-iap-jwt-assertion"
// ```
//
// This example shows how to configure custom claims to be treated as space-delimited strings.
// This is useful when JWT tokens contain custom claims with multiple space-separated values
// that should be available for individual matching in authorization policies.
//
// ```yaml
// issuer: https://example.com
// spaceDelimitedClaims:
// - "custom_scope"
// - "provider.login.scope"
// - "roles"
// ```
//
// With this configuration, a JWT containing `"custom_scope": "read write admin"` will allow
// authorization policies to match against individual values like "read", "write", or "admin".
// +kubebuilder:validation:XValidation:message="only one of jwks or jwksUri can be set",rule="oneof(self.jwksUri, self.jwks_uri, self.jwks)"
type JWTRule struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -512,9 +527,30 @@ type JWTRule struct {
OutputClaimToHeaders []*ClaimToHeader `protobuf:"bytes,11,rep,name=output_claim_to_headers,json=outputClaimToHeaders,proto3" json:"output_claim_to_headers,omitempty"` // [TODO:Update the status whenever this feature is promoted.]
// The maximum amount of time that the resolver, determined by the PILOT_JWT_ENABLE_REMOTE_JWKS environment variable,
// will spend waiting for the JWKS to be fetched. Default is 5s.
Timeout *duration.Duration `protobuf:"bytes,13,opt,name=timeout,proto3" json:"timeout,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
Timeout *duration.Duration `protobuf:"bytes,13,opt,name=timeout,proto3" json:"timeout,omitempty"`
// List of JWT claim names that should be treated as space-delimited strings.
// These claims will be split on whitespace and each individual value will be available
// for matching in authorization policies. This extends the default behavior that only
// treats 'scope' and 'permission' claims as space-delimited.
//
// Example usage for custom claims:
// ```yaml
// spaceDelimitedClaims:
// - "custom_scope"
// - "provider.login.scope"
// - "roles"
// ```
//
// This allows authorization policies to match individual values within space-separated
// claim strings, maintaining compatibility with existing JWT token formats.
//
// Note: The default claims 'scope' and 'permission' are always treated as space-delimited
// regardless of this setting.
// +protoc-gen-crd:list-value-validation:MinLength=1
// +kubebuilder:validation:MaxItems=64
SpaceDelimitedClaims []string `protobuf:"bytes,14,rep,name=space_delimited_claims,json=spaceDelimitedClaims,proto3" json:"space_delimited_claims,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *JWTRule) Reset() {
@ -624,6 +660,13 @@ func (x *JWTRule) GetTimeout() *duration.Duration {
return nil
}
func (x *JWTRule) GetSpaceDelimitedClaims() []string {
if x != nil {
return x.SpaceDelimitedClaims
}
return nil
}
// This message specifies a header location to extract JWT token.
type JWTHeader struct {
state protoimpl.MessageState `protogen:"open.v1"`
@ -752,7 +795,7 @@ const file_security_v1beta1_request_authentication_proto_rawDesc = "" +
"\n" +
"targetRefs\x18\x04 \x03(\v2).istio.type.v1beta1.PolicyTargetReferenceR\n" +
"targetRefs\x12<\n" +
"\tjwt_rules\x18\x02 \x03(\v2\x1f.istio.security.v1beta1.JWTRuleR\bjwtRules\"\xfa\x03\n" +
"\tjwt_rules\x18\x02 \x03(\v2\x1f.istio.security.v1beta1.JWTRuleR\bjwtRules\"\xb0\x04\n" +
"\aJWTRule\x12\x16\n" +
"\x06issuer\x18\x01 \x01(\tR\x06issuer\x12\x1c\n" +
"\taudiences\x18\x02 \x03(\tR\taudiences\x12\x19\n" +
@ -766,7 +809,8 @@ const file_security_v1beta1_request_authentication_proto_rawDesc = "" +
"\ffrom_cookies\x18\f \x03(\tR\vfromCookies\x124\n" +
"\x16forward_original_token\x18\t \x01(\bR\x14forwardOriginalToken\x12\\\n" +
"\x17output_claim_to_headers\x18\v \x03(\v2%.istio.security.v1beta1.ClaimToHeaderR\x14outputClaimToHeaders\x123\n" +
"\atimeout\x18\r \x01(\v2\x19.google.protobuf.DurationR\atimeout\"=\n" +
"\atimeout\x18\r \x01(\v2\x19.google.protobuf.DurationR\atimeout\x124\n" +
"\x16space_delimited_claims\x18\x0e \x03(\tR\x14spaceDelimitedClaims\"=\n" +
"\tJWTHeader\x12\x18\n" +
"\x04name\x18\x01 \x01(\tB\x04\xe2A\x01\x02R\x04name\x12\x16\n" +
"\x06prefix\x18\x02 \x01(\tR\x06prefix\"I\n" +

View File

@ -283,6 +283,17 @@ jwksUri: https://example.com/.secret/jwks.json
fromHeaders:
- &quot;x-goog-iap-jwt-assertion&quot;
</code></pre>
<p>This example shows how to configure custom claims to be treated as space-delimited strings.
This is useful when JWT tokens contain custom claims with multiple space-separated values
that should be available for individual matching in authorization policies.</p>
<pre><code class="language-yaml">issuer: https://example.com
spaceDelimitedClaims:
- &quot;custom_scope&quot;
- &quot;provider.login.scope&quot;
- &quot;roles&quot;
</code></pre>
<p>With this configuration, a JWT containing <code>&quot;custom_scope&quot;: &quot;read write admin&quot;</code> will allow
authorization policies to match against individual values like &ldquo;read&rdquo;, &ldquo;write&rdquo;, or &ldquo;admin&rdquo;.</p>
<table class="message-fields">
<thead>
@ -446,6 +457,28 @@ The header specified in each operation in the list must be unique. Nested claims
<p>The maximum amount of time that the resolver, determined by the PILOT_JWT_ENABLE_REMOTE_JWKS environment variable,
will spend waiting for the JWKS to be fetched. Default is 5s.</p>
</td>
</tr>
<tr id="JWTRule-space_delimited_claims">
<td><div class="field"><div class="name"><code><a href="#JWTRule-space_delimited_claims">spaceDelimitedClaims</a></code></div>
<div class="type">string[]</div>
</div></td>
<td>
<p>List of JWT claim names that should be treated as space-delimited strings.
These claims will be split on whitespace and each individual value will be available
for matching in authorization policies. This extends the default behavior that only
treats &lsquo;scope&rsquo; and &lsquo;permission&rsquo; claims as space-delimited.</p>
<p>Example usage for custom claims:</p>
<pre><code class="language-yaml">spaceDelimitedClaims:
- &quot;custom_scope&quot;
- &quot;provider.login.scope&quot;
- &quot;roles&quot;
</code></pre>
<p>This allows authorization policies to match individual values within space-separated
claim strings, maintaining compatibility with existing JWT token formats.</p>
<p>Note: The default claims &lsquo;scope&rsquo; and &lsquo;permission&rsquo; are always treated as space-delimited
regardless of this setting.</p>
</td>
</tr>
</tbody>

View File

@ -318,6 +318,21 @@ message RequestAuthentication {
// fromHeaders:
// - "x-goog-iap-jwt-assertion"
// ```
//
// This example shows how to configure custom claims to be treated as space-delimited strings.
// This is useful when JWT tokens contain custom claims with multiple space-separated values
// that should be available for individual matching in authorization policies.
//
// ```yaml
// issuer: https://example.com
// spaceDelimitedClaims:
// - "custom_scope"
// - "provider.login.scope"
// - "roles"
// ```
//
// With this configuration, a JWT containing `"custom_scope": "read write admin"` will allow
// authorization policies to match against individual values like "read", "write", or "admin".
// +kubebuilder:validation:XValidation:message="only one of jwks or jwksUri can be set",rule="oneof(self.jwksUri, self.jwks_uri, self.jwks)"
message JWTRule {
// Identifies the issuer that issued the JWT. See
@ -450,8 +465,30 @@ message JWTRule {
// will spend waiting for the JWKS to be fetched. Default is 5s.
google.protobuf.Duration timeout = 13;
// List of JWT claim names that should be treated as space-delimited strings.
// These claims will be split on whitespace and each individual value will be available
// for matching in authorization policies. This extends the default behavior that only
// treats 'scope' and 'permission' claims as space-delimited.
//
// Example usage for custom claims:
// ```yaml
// spaceDelimitedClaims:
// - "custom_scope"
// - "provider.login.scope"
// - "roles"
// ```
//
// This allows authorization policies to match individual values within space-separated
// claim strings, maintaining compatibility with existing JWT token formats.
//
// Note: The default claims 'scope' and 'permission' are always treated as space-delimited
// regardless of this setting.
// +protoc-gen-crd:list-value-validation:MinLength=1
// +kubebuilder:validation:MaxItems=64
repeated string space_delimited_claims = 14;
// $hide_from_docs
// Next available field number: 14
// Next available field number: 15
}
// This message specifies a header location to extract JWT token.

View File

@ -210,3 +210,14 @@ spec:
- issuer: example
timeout: "apple"
---
_err: 'spaceDelimitedClaims[0] in body should be at least 1 chars long'
apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: invalid-space-delimited-claims
spec:
jwtRules:
- issuer: example
spaceDelimitedClaims:
- ""
---

View File

@ -20,3 +20,6 @@ spec:
header: def
timeout: 5s
outputPayloadToHeader: header
spaceDelimitedClaims:
- "custom_scope"
- "provider.login.scope"