diff --git a/api/v1alpha1/oidc_types.go b/api/v1alpha1/oidc_types.go
index ecce7957627..b1de7097f86 100644
--- a/api/v1alpha1/oidc_types.go
+++ b/api/v1alpha1/oidc_types.go
@@ -30,6 +30,12 @@ type OIDC struct {
// +kubebuilder:validation:Required
ClientSecret gwapiv1b1.SecretObjectReference `json:"clientSecret"`
+ // The optional cookie name overrides to be used for Bearer and IdToken cookies in the
+ // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ // If not specified, uses a randomly generated suffix
+ // +optional
+ CookieNames *OIDCCookieNames `json:"cookieNames,omitempty"`
+
// The OIDC scopes to be used in the
// [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
// The "openid" scope is always added to the list of scopes if not already
@@ -75,3 +81,17 @@ type OIDCProvider struct {
// +optional
TokenEndpoint *string `json:"tokenEndpoint,omitempty"`
}
+
+// OIDCCookieNames defines the names of cookies to use in the Envoy OIDC filter.
+type OIDCCookieNames struct {
+ // The name of the cookie used to store the AccessToken in the
+ // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ // If not specified, defaults to "AccessToken-(randomly generated uid)"
+ // +optional
+ AccessToken *string `json:"accessToken,omitempty"`
+ // The name of the cookie used to store the IdToken in the
+ // [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ // If not specified, defaults to "IdToken-(randomly generated uid)"
+ // +optional
+ IDToken *string `json:"idToken,omitempty"`
+}
diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go
index 67918dabdfb..0aee5299921 100644
--- a/api/v1alpha1/zz_generated.deepcopy.go
+++ b/api/v1alpha1/zz_generated.deepcopy.go
@@ -3208,6 +3208,11 @@ func (in *OIDC) DeepCopyInto(out *OIDC) {
*out = *in
in.Provider.DeepCopyInto(&out.Provider)
in.ClientSecret.DeepCopyInto(&out.ClientSecret)
+ if in.CookieNames != nil {
+ in, out := &in.CookieNames, &out.CookieNames
+ *out = new(OIDCCookieNames)
+ (*in).DeepCopyInto(*out)
+ }
if in.Scopes != nil {
in, out := &in.Scopes, &out.Scopes
*out = make([]string, len(*in))
@@ -3240,6 +3245,31 @@ func (in *OIDC) DeepCopy() *OIDC {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *OIDCCookieNames) DeepCopyInto(out *OIDCCookieNames) {
+ *out = *in
+ if in.AccessToken != nil {
+ in, out := &in.AccessToken, &out.AccessToken
+ *out = new(string)
+ **out = **in
+ }
+ if in.IDToken != nil {
+ in, out := &in.IDToken, &out.IDToken
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDCCookieNames.
+func (in *OIDCCookieNames) DeepCopy() *OIDCCookieNames {
+ if in == nil {
+ return nil
+ }
+ out := new(OIDCCookieNames)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OIDCProvider) DeepCopyInto(out *OIDCProvider) {
*out = *in
diff --git a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
index 00e1b791e68..b5f9a4956ca 100644
--- a/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
+++ b/charts/gateway-helm/crds/generated/gateway.envoyproxy.io_securitypolicies.yaml
@@ -849,6 +849,25 @@ spec:
required:
- name
type: object
+ cookieNames:
+ description: |-
+ The optional cookie name overrides to be used for Bearer and IdToken cookies in the
+ [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ If not specified, uses a randomly generated suffix
+ properties:
+ accessToken:
+ description: |-
+ The name of the cookie used to store the AccessToken in the
+ [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ If not specified, defaults to "AccessToken-(randomly generated uid)"
+ type: string
+ idToken:
+ description: |-
+ The name of the cookie used to store the IdToken in the
+ [Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
+ If not specified, defaults to "IdToken-(randomly generated uid)"
+ type: string
+ type: object
logoutPath:
description: |-
The path to log a user out, clearing their credential cookies.
diff --git a/internal/gatewayapi/securitypolicy.go b/internal/gatewayapi/securitypolicy.go
index 29305d067d5..2ba9ea061ba 100644
--- a/internal/gatewayapi/securitypolicy.go
+++ b/internal/gatewayapi/securitypolicy.go
@@ -589,7 +589,9 @@ func (t *Translator) buildOIDC(
logoutPath = *oidc.LogoutPath
}
- // Generate a unique cookie suffix for oauth filters
+ // Generate a unique cookie suffix for oauth filters.
+ // This is to avoid cookie name collision when multiple security policies are applied
+ // to the same route.
suffix := utils.Digest32(string(policy.UID))
// Get the HMAC secret.
@@ -607,17 +609,18 @@ func (t *Translator) buildOIDC(
}
return &ir.OIDC{
- Name: irConfigName(policy),
- Provider: *provider,
- ClientID: oidc.ClientID,
- ClientSecret: clientSecretBytes,
- Scopes: scopes,
- Resources: oidc.Resources,
- RedirectURL: redirectURL,
- RedirectPath: redirectPath,
- LogoutPath: logoutPath,
- CookieSuffix: suffix,
- HMACSecret: hmacData,
+ Name: irConfigName(policy),
+ Provider: *provider,
+ ClientID: oidc.ClientID,
+ ClientSecret: clientSecretBytes,
+ Scopes: scopes,
+ Resources: oidc.Resources,
+ RedirectURL: redirectURL,
+ RedirectPath: redirectPath,
+ LogoutPath: logoutPath,
+ CookieSuffix: suffix,
+ CookieNameOverrides: policy.Spec.OIDC.CookieNames,
+ HMACSecret: hmacData,
}, nil
}
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.in.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.in.yaml
new file mode 100644
index 00000000000..13ce562de3b
--- /dev/null
+++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.in.yaml
@@ -0,0 +1,73 @@
+secrets:
+- apiVersion: v1
+ kind: Secret
+ metadata:
+ namespace: envoy-gateway
+ name: client1-secret
+ data:
+ client-secret: Y2xpZW50MTpzZWNyZXQK
+- apiVersion: v1
+ kind: Secret
+ metadata:
+ namespace: envoy-gateway-system
+ name: envoy-oidc-hmac
+ data:
+ hmac-secret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY=
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ namespace: envoy-gateway
+ name: gateway-1
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - name: http
+ protocol: HTTP
+ port: 80
+ allowedRoutes:
+ namespaces:
+ from: All
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ namespace: default
+ name: httproute-1
+ spec:
+ hostnames:
+ - www.example.com
+ parentRefs:
+ - namespace: envoy-gateway
+ name: gateway-1
+ sectionName: http
+ rules:
+ - matches:
+ - path:
+ value: "/foo"
+ backendRefs:
+ - name: service-1
+ port: 8080
+securityPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: SecurityPolicy
+ metadata:
+ namespace: envoy-gateway
+ name: policy-for-gateway # This policy should attach httproute-2
+ uid: b8284d0f-de82-4c65-b204-96a0d3f258a1
+ spec:
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ oidc:
+ provider:
+ issuer: "https://accounts.google.com"
+ clientID: "client1.apps.googleusercontent.com"
+ clientSecret:
+ name: "client1-secret"
+ redirectURL: "https://www.example.com/bar/oauth2/callback"
+ logoutPath: "/bar/logout"
+ cookieNames:
+ idToken: "CustomIdTokenCookie"
+ accessToken: "CustomAccessTokenCookie"
diff --git a/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.out.yaml b/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.out.yaml
new file mode 100755
index 00000000000..07ceea24fae
--- /dev/null
+++ b/internal/gatewayapi/testdata/securitypolicy-with-oidc-custom-cookies.out.yaml
@@ -0,0 +1,186 @@
+gateways:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: Gateway
+ metadata:
+ creationTimestamp: null
+ name: gateway-1
+ namespace: envoy-gateway
+ spec:
+ gatewayClassName: envoy-gateway-class
+ listeners:
+ - allowedRoutes:
+ namespaces:
+ from: All
+ name: http
+ port: 80
+ protocol: HTTP
+ status:
+ listeners:
+ - attachedRoutes: 1
+ conditions:
+ - lastTransitionTime: null
+ message: Sending translated listener configuration to the data plane
+ reason: Programmed
+ status: "True"
+ type: Programmed
+ - lastTransitionTime: null
+ message: Listener has been successfully translated
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Listener references have been resolved
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ name: http
+ supportedKinds:
+ - group: gateway.networking.k8s.io
+ kind: HTTPRoute
+ - group: gateway.networking.k8s.io
+ kind: GRPCRoute
+httpRoutes:
+- apiVersion: gateway.networking.k8s.io/v1
+ kind: HTTPRoute
+ metadata:
+ creationTimestamp: null
+ name: httproute-1
+ namespace: default
+ spec:
+ hostnames:
+ - www.example.com
+ parentRefs:
+ - name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+ rules:
+ - backendRefs:
+ - name: service-1
+ port: 8080
+ matches:
+ - path:
+ value: /foo
+ status:
+ parents:
+ - conditions:
+ - lastTransitionTime: null
+ message: Route is accepted
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ - lastTransitionTime: null
+ message: Resolved all the Object references for the Route
+ reason: ResolvedRefs
+ status: "True"
+ type: ResolvedRefs
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+ parentRef:
+ name: gateway-1
+ namespace: envoy-gateway
+ sectionName: http
+infraIR:
+ envoy-gateway/gateway-1:
+ proxy:
+ listeners:
+ - address: null
+ name: envoy-gateway/gateway-1/http
+ ports:
+ - containerPort: 10080
+ name: http-80
+ protocol: HTTP
+ servicePort: 80
+ metadata:
+ labels:
+ gateway.envoyproxy.io/owning-gateway-name: gateway-1
+ gateway.envoyproxy.io/owning-gateway-namespace: envoy-gateway
+ name: envoy-gateway/gateway-1
+securityPolicies:
+- apiVersion: gateway.envoyproxy.io/v1alpha1
+ kind: SecurityPolicy
+ metadata:
+ creationTimestamp: null
+ name: policy-for-gateway
+ namespace: envoy-gateway
+ uid: b8284d0f-de82-4c65-b204-96a0d3f258a1
+ spec:
+ oidc:
+ clientID: client1.apps.googleusercontent.com
+ clientSecret:
+ group: null
+ kind: null
+ name: client1-secret
+ cookieNames:
+ accessToken: CustomAccessTokenCookie
+ idToken: CustomIdTokenCookie
+ logoutPath: /bar/logout
+ provider:
+ issuer: https://accounts.google.com
+ redirectURL: https://www.example.com/bar/oauth2/callback
+ targetRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ status:
+ ancestors:
+ - ancestorRef:
+ group: gateway.networking.k8s.io
+ kind: Gateway
+ name: gateway-1
+ namespace: envoy-gateway
+ conditions:
+ - lastTransitionTime: null
+ message: Policy has been accepted.
+ reason: Accepted
+ status: "True"
+ type: Accepted
+ controllerName: gateway.envoyproxy.io/gatewayclass-controller
+xdsIR:
+ envoy-gateway/gateway-1:
+ accessLog:
+ text:
+ - path: /dev/stdout
+ http:
+ - address: 0.0.0.0
+ hostnames:
+ - '*'
+ isHTTP2: false
+ name: envoy-gateway/gateway-1/http
+ path:
+ escapedSlashesAction: UnescapeAndRedirect
+ mergeSlashes: true
+ port: 10080
+ routes:
+ - destination:
+ name: httproute/default/httproute-1/rule/0
+ settings:
+ - addressType: IP
+ endpoints:
+ - host: 7.7.7.7
+ port: 8080
+ protocol: HTTP
+ weight: 1
+ hostname: www.example.com
+ isHTTP2: false
+ name: httproute/default/httproute-1/rule/0/match/0/www_example_com
+ pathMatch:
+ distinct: false
+ name: ""
+ prefix: /foo
+ security:
+ oidc:
+ clientID: client1.apps.googleusercontent.com
+ clientSecret: Y2xpZW50MTpzZWNyZXQK
+ cookieNameOverrides:
+ accessToken: CustomAccessTokenCookie
+ idToken: CustomIdTokenCookie
+ cookieSuffix: b0a1b740
+ hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY=
+ logoutPath: /bar/logout
+ name: securitypolicy/envoy-gateway/policy-for-gateway
+ provider:
+ authorizationEndpoint: https://accounts.google.com/o/oauth2/v2/auth
+ tokenEndpoint: https://oauth2.googleapis.com/token
+ redirectPath: /bar/oauth2/callback
+ redirectURL: https://www.example.com/bar/oauth2/callback
+ scopes:
+ - openid
diff --git a/internal/ir/xds.go b/internal/ir/xds.go
index 77505d57561..1fbebde2442 100644
--- a/internal/ir/xds.go
+++ b/internal/ir/xds.go
@@ -686,9 +686,12 @@ type OIDC struct {
// CookieSuffix will be added to the name of the cookies set by the oauth filter.
// Adding a suffix avoids multiple oauth filters from overwriting each other's cookies.
- // These cookies are set by the oauth filter, including: BearerToken,
+ // These cookies are set by the oauth filter, including: AccessToken,
// OauthHMAC, OauthExpires, IdToken, and RefreshToken.
CookieSuffix string `json:"cookieSuffix,omitempty"`
+
+ // CookieNameOverrides can optionally override the generated name of the cookies set by the oauth filter.
+ CookieNameOverrides *egv1a1.OIDCCookieNames `json:"cookieNameOverrides,omitempty"`
}
type OIDCProvider struct {
diff --git a/internal/ir/zz_generated.deepcopy.go b/internal/ir/zz_generated.deepcopy.go
index 7d93ad057ff..94aca3ecc26 100644
--- a/internal/ir/zz_generated.deepcopy.go
+++ b/internal/ir/zz_generated.deepcopy.go
@@ -1492,6 +1492,11 @@ func (in *OIDC) DeepCopyInto(out *OIDC) {
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.CookieNameOverrides != nil {
+ in, out := &in.CookieNameOverrides, &out.CookieNameOverrides
+ *out = new(v1alpha1.OIDCCookieNames)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OIDC.
diff --git a/internal/xds/translator/oidc.go b/internal/xds/translator/oidc.go
index 183fb7944aa..ae54603f57b 100644
--- a/internal/xds/translator/oidc.go
+++ b/internal/xds/translator/oidc.go
@@ -159,7 +159,7 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) {
},
},
CookieNames: &oauth2v3.OAuth2Credentials_CookieNames{
- BearerToken: fmt.Sprintf("BearerToken-%s", oidc.CookieSuffix),
+ BearerToken: fmt.Sprintf("AccessToken-%s", oidc.CookieSuffix),
OauthHmac: fmt.Sprintf("OauthHMAC-%s", oidc.CookieSuffix),
OauthExpires: fmt.Sprintf("OauthExpires-%s", oidc.CookieSuffix),
IdToken: fmt.Sprintf("IdToken-%s", oidc.CookieSuffix),
@@ -172,6 +172,17 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) {
Resources: oidc.Resources,
},
}
+
+ if oidc.CookieNameOverrides != nil &&
+ oidc.CookieNameOverrides.AccessToken != nil {
+ oauth2.Config.Credentials.CookieNames.BearerToken = *oidc.CookieNameOverrides.AccessToken
+ }
+
+ if oidc.CookieNameOverrides != nil &&
+ oidc.CookieNameOverrides.IDToken != nil {
+ oauth2.Config.Credentials.CookieNames.IdToken = *oidc.CookieNameOverrides.IDToken
+ }
+
return oauth2, nil
}
diff --git a/internal/xds/translator/testdata/in/xds-ir/oidc.yaml b/internal/xds/translator/testdata/in/xds-ir/oidc.yaml
index b2e395f8320..68e36bffab1 100644
--- a/internal/xds/translator/testdata/in/xds-ir/oidc.yaml
+++ b/internal/xds/translator/testdata/in/xds-ir/oidc.yaml
@@ -66,3 +66,6 @@ http:
redirectPath: "/bar/oauth2/callback"
logoutPath: "/bar/logout"
cookieSuffix: 5f93c2e4
+ cookieNameOverrides:
+ idToken: "CustomIdTokenOverride"
+ accessToken: "CustomAccessTokenOverride"
diff --git a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml
index 1365c290cb7..a06b7265316 100755
--- a/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml
@@ -117,7 +117,7 @@
credentials:
clientId: client.oauth.foo.com
cookieNames:
- bearerToken: BearerToken-5F93C2E4
+ bearerToken: AccessToken-5F93C2E4
idToken: IdToken-5F93C2E4
oauthExpires: OauthExpires-5F93C2E4
oauthHmac: OauthHMAC-5F93C2E4
diff --git a/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
index 95e075f047e..ab8556ba154 100644
--- a/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
+++ b/internal/xds/translator/testdata/out/xds-ir/oidc.listeners.yaml
@@ -28,7 +28,7 @@
credentials:
clientId: client.oauth.foo.com
cookieNames:
- bearerToken: BearerToken-5F93C2E4
+ bearerToken: AccessToken-5F93C2E4
idToken: IdToken-5F93C2E4
oauthExpires: OauthExpires-5F93C2E4
oauthHmac: OauthHMAC-5F93C2E4
@@ -71,8 +71,8 @@
credentials:
clientId: client.oauth.bar.com
cookieNames:
- bearerToken: BearerToken-5f93c2e4
- idToken: IdToken-5f93c2e4
+ bearerToken: CustomAccessTokenOverride
+ idToken: CustomIdTokenOverride
oauthExpires: OauthExpires-5f93c2e4
oauthHmac: OauthHMAC-5f93c2e4
refreshToken: RefreshToken-5f93c2e4
diff --git a/site/content/en/latest/api/extension_types.md b/site/content/en/latest/api/extension_types.md
index d28b28339dd..c9bbefd18cc 100644
--- a/site/content/en/latest/api/extension_types.md
+++ b/site/content/en/latest/api/extension_types.md
@@ -2313,12 +2313,28 @@ _Appears in:_
| `provider` | _[OIDCProvider](#oidcprovider)_ | true | The OIDC Provider configuration. |
| `clientID` | _string_ | true | The client ID to be used in the OIDC
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). |
| `clientSecret` | _[SecretObjectReference](https://gateway-api.sigs.k8s.io/references/spec/#gateway.networking.k8s.io/v1.SecretObjectReference)_ | true | The Kubernetes secret which contains the OIDC client secret to be used in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
This is an Opaque secret. The client secret should be stored in the key
"client-secret". |
+| `cookieNames` | _[OIDCCookieNames](#oidccookienames)_ | false | The optional cookie name overrides to be used for Bearer and IdToken cookies in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
If not specified, uses a randomly generated suffix |
| `scopes` | _string array_ | false | The OIDC scopes to be used in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
The "openid" scope is always added to the list of scopes if not already
specified. |
| `resources` | _string array_ | false | The OIDC resources to be used in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). |
| `redirectURL` | _string_ | true | The redirect URL to be used in the OIDC
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
If not specified, uses the default redirect URI "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" |
| `logoutPath` | _string_ | true | The path to log a user out, clearing their credential cookies.
If not specified, uses a default logout path "/logout" |
+#### OIDCCookieNames
+
+
+
+OIDCCookieNames defines the names of cookies to use in the Envoy OIDC filter.
+
+_Appears in:_
+- [OIDC](#oidc)
+
+| Field | Type | Required | Description |
+| --- | --- | --- | --- |
+| `accessToken` | _string_ | false | The name of the cookie used to store the AccessToken in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
If not specified, defaults to "AccessToken-(randomly generated uid)" |
+| `idToken` | _string_ | false | The name of the cookie used to store the IdToken in the
[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).
If not specified, defaults to "IdToken-(randomly generated uid)" |
+
+
#### OIDCProvider