Skip to content

Commit d86cf30

Browse files
committed
feat: add access token support in the OIDC
fix: virtual server template for OIDC.AccessTokenEnable
1 parent ff7fd17 commit d86cf30

File tree

12 files changed

+246
-192
lines changed

12 files changed

+246
-192
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ spec:
134134
type: string
135135
zoneSyncLeeway:
136136
type: integer
137+
accessTokenEnable:
138+
type: boolean
137139
rateLimit:
138140
description: RateLimit defines a rate limit policy.
139141
type: object

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ spec:
134134
type: string
135135
zoneSyncLeeway:
136136
type: integer
137+
accessTokenEnable:
138+
type: boolean
137139
rateLimit:
138140
description: RateLimit defines a rate limit policy.
139141
type: object

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: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,14 @@ function auth(r, afterSyncCheck) {
101101
return;
102102
}
103103

104-
// ID Token is valid, update keyval
104+
// Update ID token and access token in the key-value store
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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,11 @@ server {
423423
{{ $proxyOrGRPC }}_set_header username $jwt_claim_sub;
424424
{{ end }}
425425

426+
{{ if $s.OIDC.AccessTokenEnable }}
427+
{{ $proxyOrGRPC }}_set_header Authorization "Bearer $access_token";
428+
{{ $proxyOrGRPC }}_intercept_errors on;
429+
{{ end }}
430+
426431
{{ with $l.WAF }}
427432
app_protect_enable {{ .Enable }};
428433
{{ if .ApPolicy }}

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
},
@@ -4203,10 +4204,11 @@ func TestGeneratePoliciesFails(t *testing.T) {
42034204
},
42044205
Spec: conf_v1.PolicySpec{
42054206
OIDC: &conf_v1.OIDC{
4206-
ClientSecret: "oidc-secret",
4207-
AuthEndpoint: "http://foo.com/bar",
4208-
TokenEndpoint: "http://foo.com/bar",
4209-
JWKSURI: "http://foo.com/bar",
4207+
ClientSecret: "oidc-secret",
4208+
AuthEndpoint: "http://foo.com/bar",
4209+
TokenEndpoint: "http://foo.com/bar",
4210+
JWKSURI: "http://foo.com/bar",
4211+
AccessTokenEnable: true,
42104212
},
42114213
},
42124214
},
@@ -4249,11 +4251,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
42494251
},
42504252
Spec: conf_v1.PolicySpec{
42514253
OIDC: &conf_v1.OIDC{
4252-
ClientID: "foo",
4253-
ClientSecret: "oidc-secret",
4254-
AuthEndpoint: "https://foo.com/auth",
4255-
TokenEndpoint: "https://foo.com/token",
4256-
JWKSURI: "https://foo.com/certs",
4254+
ClientID: "foo",
4255+
ClientSecret: "oidc-secret",
4256+
AuthEndpoint: "https://foo.com/auth",
4257+
TokenEndpoint: "https://foo.com/token",
4258+
JWKSURI: "https://foo.com/certs",
4259+
AccessTokenEnable: true,
42574260
},
42584261
},
42594262
},
@@ -4264,11 +4267,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
42644267
},
42654268
Spec: conf_v1.PolicySpec{
42664269
OIDC: &conf_v1.OIDC{
4267-
ClientID: "foo",
4268-
ClientSecret: "oidc-secret",
4269-
AuthEndpoint: "https://bar.com/auth",
4270-
TokenEndpoint: "https://bar.com/token",
4271-
JWKSURI: "https://bar.com/certs",
4270+
ClientID: "foo",
4271+
ClientSecret: "oidc-secret",
4272+
AuthEndpoint: "https://bar.com/auth",
4273+
TokenEndpoint: "https://bar.com/token",
4274+
JWKSURI: "https://bar.com/certs",
4275+
AccessTokenEnable: true,
42724276
},
42734277
},
42744278
},
@@ -4288,14 +4292,15 @@ func TestGeneratePoliciesFails(t *testing.T) {
42884292
context: "route",
42894293
oidcPolCfg: &oidcPolicyCfg{
42904294
oidc: &version2.OIDC{
4291-
AuthEndpoint: "https://foo.com/auth",
4292-
TokenEndpoint: "https://foo.com/token",
4293-
JwksURI: "https://foo.com/certs",
4294-
ClientID: "foo",
4295-
ClientSecret: "super_secret_123",
4296-
RedirectURI: "/_codexch",
4297-
Scope: "openid",
4298-
ZoneSyncLeeway: 0,
4295+
AuthEndpoint: "https://foo.com/auth",
4296+
TokenEndpoint: "https://foo.com/token",
4297+
JwksURI: "https://foo.com/certs",
4298+
ClientID: "foo",
4299+
ClientSecret: "super_secret_123",
4300+
RedirectURI: "/_codexch",
4301+
Scope: "openid",
4302+
ZoneSyncLeeway: 0,
4303+
AccessTokenEnable: true,
42994304
},
43004305
key: "default/oidc-policy-1",
43014306
},
@@ -4311,13 +4316,14 @@ func TestGeneratePoliciesFails(t *testing.T) {
43114316
},
43124317
expectedOidc: &oidcPolicyCfg{
43134318
oidc: &version2.OIDC{
4314-
AuthEndpoint: "https://foo.com/auth",
4315-
TokenEndpoint: "https://foo.com/token",
4316-
JwksURI: "https://foo.com/certs",
4317-
ClientID: "foo",
4318-
ClientSecret: "super_secret_123",
4319-
RedirectURI: "/_codexch",
4320-
Scope: "openid",
4319+
AuthEndpoint: "https://foo.com/auth",
4320+
TokenEndpoint: "https://foo.com/token",
4321+
JwksURI: "https://foo.com/certs",
4322+
ClientID: "foo",
4323+
ClientSecret: "super_secret_123",
4324+
RedirectURI: "/_codexch",
4325+
Scope: "openid",
4326+
AccessTokenEnable: true,
43214327
},
43224328
key: "default/oidc-policy-1",
43234329
},
@@ -4342,11 +4348,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
43424348
},
43434349
Spec: conf_v1.PolicySpec{
43444350
OIDC: &conf_v1.OIDC{
4345-
ClientSecret: "oidc-secret",
4346-
AuthEndpoint: "https://foo.com/auth",
4347-
TokenEndpoint: "https://foo.com/token",
4348-
JWKSURI: "https://foo.com/certs",
4349-
ClientID: "foo",
4351+
ClientSecret: "oidc-secret",
4352+
AuthEndpoint: "https://foo.com/auth",
4353+
TokenEndpoint: "https://foo.com/token",
4354+
JWKSURI: "https://foo.com/certs",
4355+
ClientID: "foo",
4356+
AccessTokenEnable: true,
43504357
},
43514358
},
43524359
},
@@ -4357,11 +4364,12 @@ func TestGeneratePoliciesFails(t *testing.T) {
43574364
},
43584365
Spec: conf_v1.PolicySpec{
43594366
OIDC: &conf_v1.OIDC{
4360-
ClientSecret: "oidc-secret",
4361-
AuthEndpoint: "https://bar.com/auth",
4362-
TokenEndpoint: "https://bar.com/token",
4363-
JWKSURI: "https://bar.com/certs",
4364-
ClientID: "bar",
4367+
ClientSecret: "oidc-secret",
4368+
AuthEndpoint: "https://bar.com/auth",
4369+
TokenEndpoint: "https://bar.com/token",
4370+
JWKSURI: "https://bar.com/certs",
4371+
ClientID: "bar",
4372+
AccessTokenEnable: true,
43654373
},
43664374
},
43674375
},
@@ -4389,14 +4397,15 @@ func TestGeneratePoliciesFails(t *testing.T) {
43894397
},
43904398
expectedOidc: &oidcPolicyCfg{
43914399
&version2.OIDC{
4392-
AuthEndpoint: "https://foo.com/auth",
4393-
TokenEndpoint: "https://foo.com/token",
4394-
JwksURI: "https://foo.com/certs",
4395-
ClientID: "foo",
4396-
ClientSecret: "super_secret_123",
4397-
RedirectURI: "/_codexch",
4398-
Scope: "openid",
4399-
ZoneSyncLeeway: 200,
4400+
AuthEndpoint: "https://foo.com/auth",
4401+
TokenEndpoint: "https://foo.com/token",
4402+
JwksURI: "https://foo.com/certs",
4403+
ClientID: "foo",
4404+
ClientSecret: "super_secret_123",
4405+
RedirectURI: "/_codexch",
4406+
Scope: "openid",
4407+
ZoneSyncLeeway: 200,
4408+
AccessTokenEnable: true,
44004409
},
44014410
"default/oidc-policy",
44024411
},

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)