Skip to content

Commit 4dfccbe

Browse files
authored
Add access token support in the OIDC (#3474)
1 parent 2b40efe commit 4dfccbe

File tree

12 files changed

+243
-191
lines changed

12 files changed

+243
-191
lines changed

deployments/common/crds/k8s.nginx.org_policies.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ spec:
114114
description: OIDC defines an Open ID Connect policy.
115115
type: object
116116
properties:
117+
accessTokenEnable:
118+
type: boolean
117119
authEndpoint:
118120
type: string
119121
authExtraArgs:

deployments/helm-chart/crds/k8s.nginx.org_policies.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ spec:
114114
description: OIDC defines an Open ID Connect policy.
115115
type: object
116116
properties:
117+
accessTokenEnable:
118+
type: boolean
117119
authEndpoint:
118120
type: string
119121
authExtraArgs:

docs/content/configuration/policy-resource.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,7 @@ spec:
356356
authEndpoint: https://idp.example.com/openid-connect/auth
357357
tokenEndpoint: https://idp.example.com/openid-connect/token
358358
jwksURI: https://idp.example.com/openid-connect/certs
359+
accessTokenEnable: true
359360
```
360361

361362
NGINX Plus will pass the ID of an authenticated user to the backend in the HTTP header `username`.
@@ -384,7 +385,8 @@ The OIDC policy defines a few internal locations that can't be customized: `/_jw
384385
|``jwksURI`` | URL for the JSON Web Key Set (JWK) document provided by your OpenID Connect provider. | ``string`` | Yes |
385386
|``scope`` | List of OpenID Connect scopes. Possible values are ``openid``, ``profile``, ``email``, ``address`` and ``phone``. The scope ``openid`` always needs to be present and others can be added concatenating them with a ``+`` sign, for example ``openid+profile+email``. The default is ``openid``. | ``string`` | No |
386387
|``redirectURI`` | Allows overriding the default redirect URI. The default is ``/_codexch``. | ``string`` | No |
387-
|``zoneSyncLeeway`` | Specifies the maximum timeout in milliseconds for synchronizing ID tokens and shared values between Ingress Controller pods. The default is ``200``. | ``int`` | No |
388+
|``zoneSyncLeeway`` | Specifies the maximum timeout in milliseconds for synchronizing ID/access tokens and shared values between Ingress Controller pods. The default is ``200``. | ``int`` | No |
389+
|``accessTokenEnable`` | Option of whether Bearer token is used to authorize NGINX to access protected backend. | ``boolean`` | No |
388390
{{% /table %}}
389391

390392
> **Note**: Only one OIDC policy can be referenced in a VirtualServer and its VirtualServerRoutes. However, the same policy can still be applied to different routes in the VirtualServer and VirtualServerRoutes.

examples/custom-resources/oidc/oidc.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ spec:
1010
tokenEndpoint: http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/token
1111
jwksURI: http://keycloak.default.svc.cluster.local:8080/auth/realms/master/protocol/openid-connect/certs
1212
scope: openid+profile+email
13+
accessTokenEnable: true

internal/configs/oidc/oidc_common.conf

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,17 @@ map $http_x_forwarded_proto $proto {
1717
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m;
1818

1919
# Change timeout values to at least the validity period of each token type
20-
keyval_zone zone=oidc_id_tokens:1M timeout=1h sync;
21-
keyval_zone zone=refresh_tokens:1M timeout=8h sync;
20+
keyval_zone zone=oidc_id_tokens:1M timeout=1h sync;
21+
keyval_zone zone=oidc_access_tokens:1M timeout=1h sync;
22+
keyval_zone zone=refresh_tokens:1M timeout=8h sync;
2223
#keyval_zone zone=oidc_pkce:128K timeout=90s sync; # Temporary storage for PKCE code verifier.
2324

24-
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
25-
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
26-
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
27-
keyval $request_id $new_refresh zone=refresh_tokens; # ''
25+
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for ID token(JWT)
26+
keyval $cookie_auth_token $access_token zone=oidc_access_tokens; # Exchange cookie for access token
27+
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
28+
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
29+
keyval $request_id $new_access_token zone=oidc_access_tokens;
30+
keyval $request_id $new_refresh zone=refresh_tokens; # ''
2831
#keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
2932

3033
auth_jwt_claim_set $jwt_audience aud; # In case aud is an array

internal/configs/oidc/openid_connect.js

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,12 @@ function auth(r, afterSyncCheck) {
103103

104104
// ID Token is valid, update keyval
105105
r.log("OIDC refresh success, updating id_token for " + r.variables.cookie_auth_token);
106-
r.variables.session_jwt = tokenset.id_token; // Update key-value store
106+
r.variables.session_jwt = tokenset.id_token;
107+
if (tokenset.access_token) {
108+
r.variables.access_token = tokenset.access_token;
109+
} else {
110+
r.variables.access_token = "";
111+
}
107112

108113
// Update refresh token (if we got a new one)
109114
if (r.variables.refresh_token != tokenset.refresh_token) {
@@ -187,6 +192,11 @@ function codeExchange(r) {
187192
// Add opaque token to keyval session store
188193
r.log("OIDC success, creating session " + r.variables.request_id);
189194
r.variables.new_session = tokenset.id_token; // Create key-value store entry
195+
if (tokenset.access_token) {
196+
r.variables.new_access_token = tokenset.access_token;
197+
} else {
198+
r.variables.new_access_token = "";
199+
}
190200
r.headersOut["Set-Cookie"] = "auth_token=" + r.variables.request_id + "; " + r.variables.oidc_cookie_flags;
191201
r.return(302, r.variables.redirect_base + r.variables.cookie_auth_redir);
192202
}
@@ -256,6 +266,7 @@ function validateIdToken(r) {
256266
function logout(r) {
257267
r.log("OIDC logout for " + r.variables.cookie_auth_token);
258268
r.variables.session_jwt = "-";
269+
r.variables.access_token = "-";
259270
r.variables.refresh_token = "-";
260271
r.return(302, r.variables.oidc_logout_redirect);
261272
}

internal/configs/version2/http.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,16 @@ type EgressMTLS struct {
111111

112112
// OIDC holds OIDC configuration data.
113113
type OIDC struct {
114-
AuthEndpoint string
115-
ClientID string
116-
ClientSecret string
117-
JwksURI string
118-
Scope string
119-
TokenEndpoint string
120-
RedirectURI string
121-
ZoneSyncLeeway int
122-
AuthExtraArgs string
114+
AuthEndpoint string
115+
ClientID string
116+
ClientSecret string
117+
JwksURI string
118+
Scope string
119+
TokenEndpoint string
120+
RedirectURI string
121+
ZoneSyncLeeway int
122+
AuthExtraArgs string
123+
AccessTokenEnable bool
123124
}
124125

125126
// WAF defines WAF configuration.

internal/configs/version2/nginx-plus.virtualserver.tmpl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,9 @@ server {
427427
error_page 401 = @do_oidc_flow;
428428
auth_jwt_key_request /_jwks_uri;
429429
{{ $proxyOrGRPC }}_set_header username $jwt_claim_sub;
430+
{{- if $s.OIDC.AccessTokenEnable }}
431+
{{ $proxyOrGRPC }}_set_header Authorization "Bearer $access_token";
432+
{{- end }}
430433
{{ end }}
431434

432435
{{ with $l.WAF }}

internal/configs/virtualserver.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,15 +1052,16 @@ func (p *policiesCfg) addOIDCConfig(
10521052
}
10531053

10541054
oidcPolCfg.oidc = &version2.OIDC{
1055-
AuthEndpoint: oidc.AuthEndpoint,
1056-
AuthExtraArgs: authExtraArgs,
1057-
TokenEndpoint: oidc.TokenEndpoint,
1058-
JwksURI: oidc.JWKSURI,
1059-
ClientID: oidc.ClientID,
1060-
ClientSecret: string(clientSecret),
1061-
Scope: scope,
1062-
RedirectURI: redirectURI,
1063-
ZoneSyncLeeway: generateIntFromPointer(oidc.ZoneSyncLeeway, 200),
1055+
AuthEndpoint: oidc.AuthEndpoint,
1056+
AuthExtraArgs: authExtraArgs,
1057+
TokenEndpoint: oidc.TokenEndpoint,
1058+
JwksURI: oidc.JWKSURI,
1059+
ClientID: oidc.ClientID,
1060+
ClientSecret: string(clientSecret),
1061+
Scope: scope,
1062+
RedirectURI: redirectURI,
1063+
ZoneSyncLeeway: generateIntFromPointer(oidc.ZoneSyncLeeway, 200),
1064+
AccessTokenEnable: oidc.AccessTokenEnable,
10641065
}
10651066
oidcPolCfg.key = polKey
10661067
}

internal/configs/virtualserver_test.go

Lines changed: 64 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3136,14 +3136,15 @@ func TestGeneratePolicies(t *testing.T) {
31363136
},
31373137
Spec: conf_v1.PolicySpec{
31383138
OIDC: &conf_v1.OIDC{
3139-
AuthEndpoint: "http://example.com/auth",
3140-
TokenEndpoint: "http://example.com/token",
3141-
JWKSURI: "http://example.com/jwks",
3142-
ClientID: "client-id",
3143-
ClientSecret: "oidc-secret",
3144-
Scope: "scope",
3145-
RedirectURI: "/redirect",
3146-
ZoneSyncLeeway: createPointerFromInt(20),
3139+
AuthEndpoint: "http://example.com/auth",
3140+
TokenEndpoint: "http://example.com/token",
3141+
JWKSURI: "http://example.com/jwks",
3142+
ClientID: "client-id",
3143+
ClientSecret: "oidc-secret",
3144+
Scope: "scope",
3145+
RedirectURI: "/redirect",
3146+
ZoneSyncLeeway: createPointerFromInt(20),
3147+
AccessTokenEnable: true,
31473148
},
31483149
},
31493150
},
@@ -4252,10 +4253,11 @@ func TestGeneratePoliciesFails(t *testing.T) {
42524253
},
42534254
Spec: conf_v1.PolicySpec{
42544255
OIDC: &conf_v1.OIDC{
4255-
ClientSecret: "oidc-secret",
4256-
AuthEndpoint: "http://foo.com/bar",
4257-
TokenEndpoint: "http://foo.com/bar",
4258-
JWKSURI: "http://foo.com/bar",
4256+
ClientSecret: "oidc-secret",
4257+
AuthEndpoint: "http://foo.com/bar",
4258+
TokenEndpoint: "http://foo.com/bar",
4259+
JWKSURI: "http://foo.com/bar",
4260+
AccessTokenEnable: true,
42594261
},
42604262
},
42614263
},
@@ -4298,11 +4300,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
42984300
},
42994301
Spec: conf_v1.PolicySpec{
43004302
OIDC: &conf_v1.OIDC{
4301-
ClientID: "foo",
4302-
ClientSecret: "oidc-secret",
4303-
AuthEndpoint: "https://foo.com/auth",
4304-
TokenEndpoint: "https://foo.com/token",
4305-
JWKSURI: "https://foo.com/certs",
4303+
ClientID: "foo",
4304+
ClientSecret: "oidc-secret",
4305+
AuthEndpoint: "https://foo.com/auth",
4306+
TokenEndpoint: "https://foo.com/token",
4307+
JWKSURI: "https://foo.com/certs",
4308+
AccessTokenEnable: true,
43064309
},
43074310
},
43084311
},
@@ -4313,11 +4316,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
43134316
},
43144317
Spec: conf_v1.PolicySpec{
43154318
OIDC: &conf_v1.OIDC{
4316-
ClientID: "foo",
4317-
ClientSecret: "oidc-secret",
4318-
AuthEndpoint: "https://bar.com/auth",
4319-
TokenEndpoint: "https://bar.com/token",
4320-
JWKSURI: "https://bar.com/certs",
4319+
ClientID: "foo",
4320+
ClientSecret: "oidc-secret",
4321+
AuthEndpoint: "https://bar.com/auth",
4322+
TokenEndpoint: "https://bar.com/token",
4323+
JWKSURI: "https://bar.com/certs",
4324+
AccessTokenEnable: true,
43214325
},
43224326
},
43234327
},
@@ -4337,14 +4341,15 @@ func TestGeneratePoliciesFails(t *testing.T) {
43374341
context: "route",
43384342
oidcPolCfg: &oidcPolicyCfg{
43394343
oidc: &version2.OIDC{
4340-
AuthEndpoint: "https://foo.com/auth",
4341-
TokenEndpoint: "https://foo.com/token",
4342-
JwksURI: "https://foo.com/certs",
4343-
ClientID: "foo",
4344-
ClientSecret: "super_secret_123",
4345-
RedirectURI: "/_codexch",
4346-
Scope: "openid",
4347-
ZoneSyncLeeway: 0,
4344+
AuthEndpoint: "https://foo.com/auth",
4345+
TokenEndpoint: "https://foo.com/token",
4346+
JwksURI: "https://foo.com/certs",
4347+
ClientID: "foo",
4348+
ClientSecret: "super_secret_123",
4349+
RedirectURI: "/_codexch",
4350+
Scope: "openid",
4351+
ZoneSyncLeeway: 0,
4352+
AccessTokenEnable: true,
43484353
},
43494354
key: "default/oidc-policy-1",
43504355
},
@@ -4360,13 +4365,14 @@ func TestGeneratePoliciesFails(t *testing.T) {
43604365
},
43614366
expectedOidc: &oidcPolicyCfg{
43624367
oidc: &version2.OIDC{
4363-
AuthEndpoint: "https://foo.com/auth",
4364-
TokenEndpoint: "https://foo.com/token",
4365-
JwksURI: "https://foo.com/certs",
4366-
ClientID: "foo",
4367-
ClientSecret: "super_secret_123",
4368-
RedirectURI: "/_codexch",
4369-
Scope: "openid",
4368+
AuthEndpoint: "https://foo.com/auth",
4369+
TokenEndpoint: "https://foo.com/token",
4370+
JwksURI: "https://foo.com/certs",
4371+
ClientID: "foo",
4372+
ClientSecret: "super_secret_123",
4373+
RedirectURI: "/_codexch",
4374+
Scope: "openid",
4375+
AccessTokenEnable: true,
43704376
},
43714377
key: "default/oidc-policy-1",
43724378
},
@@ -4391,11 +4397,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
43914397
},
43924398
Spec: conf_v1.PolicySpec{
43934399
OIDC: &conf_v1.OIDC{
4394-
ClientSecret: "oidc-secret",
4395-
AuthEndpoint: "https://foo.com/auth",
4396-
TokenEndpoint: "https://foo.com/token",
4397-
JWKSURI: "https://foo.com/certs",
4398-
ClientID: "foo",
4400+
ClientSecret: "oidc-secret",
4401+
AuthEndpoint: "https://foo.com/auth",
4402+
TokenEndpoint: "https://foo.com/token",
4403+
JWKSURI: "https://foo.com/certs",
4404+
ClientID: "foo",
4405+
AccessTokenEnable: true,
43994406
},
44004407
},
44014408
},
@@ -4406,11 +4413,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
44064413
},
44074414
Spec: conf_v1.PolicySpec{
44084415
OIDC: &conf_v1.OIDC{
4409-
ClientSecret: "oidc-secret",
4410-
AuthEndpoint: "https://bar.com/auth",
4411-
TokenEndpoint: "https://bar.com/token",
4412-
JWKSURI: "https://bar.com/certs",
4413-
ClientID: "bar",
4416+
ClientSecret: "oidc-secret",
4417+
AuthEndpoint: "https://bar.com/auth",
4418+
TokenEndpoint: "https://bar.com/token",
4419+
JWKSURI: "https://bar.com/certs",
4420+
ClientID: "bar",
4421+
AccessTokenEnable: true,
44144422
},
44154423
},
44164424
},
@@ -4438,14 +4446,15 @@ func TestGeneratePoliciesFails(t *testing.T) {
44384446
},
44394447
expectedOidc: &oidcPolicyCfg{
44404448
&version2.OIDC{
4441-
AuthEndpoint: "https://foo.com/auth",
4442-
TokenEndpoint: "https://foo.com/token",
4443-
JwksURI: "https://foo.com/certs",
4444-
ClientID: "foo",
4445-
ClientSecret: "super_secret_123",
4446-
RedirectURI: "/_codexch",
4447-
Scope: "openid",
4448-
ZoneSyncLeeway: 200,
4449+
AuthEndpoint: "https://foo.com/auth",
4450+
TokenEndpoint: "https://foo.com/token",
4451+
JwksURI: "https://foo.com/certs",
4452+
ClientID: "foo",
4453+
ClientSecret: "super_secret_123",
4454+
RedirectURI: "/_codexch",
4455+
Scope: "openid",
4456+
ZoneSyncLeeway: 200,
4457+
AccessTokenEnable: true,
44494458
},
44504459
"default/oidc-policy",
44514460
},

pkg/apis/configuration/v1/types.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -475,15 +475,16 @@ type EgressMTLS struct {
475475

476476
// OIDC defines an Open ID Connect policy.
477477
type OIDC struct {
478-
AuthEndpoint string `json:"authEndpoint"`
479-
TokenEndpoint string `json:"tokenEndpoint"`
480-
JWKSURI string `json:"jwksURI"`
481-
ClientID string `json:"clientID"`
482-
ClientSecret string `json:"clientSecret"`
483-
Scope string `json:"scope"`
484-
RedirectURI string `json:"redirectURI"`
485-
ZoneSyncLeeway *int `json:"zoneSyncLeeway"`
486-
AuthExtraArgs []string `json:"authExtraArgs"`
478+
AuthEndpoint string `json:"authEndpoint"`
479+
TokenEndpoint string `json:"tokenEndpoint"`
480+
JWKSURI string `json:"jwksURI"`
481+
ClientID string `json:"clientID"`
482+
ClientSecret string `json:"clientSecret"`
483+
Scope string `json:"scope"`
484+
RedirectURI string `json:"redirectURI"`
485+
ZoneSyncLeeway *int `json:"zoneSyncLeeway"`
486+
AuthExtraArgs []string `json:"authExtraArgs"`
487+
AccessTokenEnable bool `json:"accessTokenEnable"`
487488
}
488489

489490
// WAF defines an WAF policy.

0 commit comments

Comments
 (0)