Skip to content

Commit 1c0f658

Browse files
committed
Access token support
1 parent 6ad8ec6 commit 1c0f658

File tree

4 files changed

+63
-11
lines changed

4 files changed

+63
-11
lines changed

README.md

+31-2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,21 @@ Subsequent requests to protected resources are authenticated by exchanging the s
3030

3131
For more information on OpenID Connect and JWT validation with NGINX Plus, see [Authenticating Users to Existing Applications with OpenID Connect and NGINX Plus](https://www.nginx.com/blog/authenticating-users-existing-applications-openid-connect-nginx-plus/).
3232

33+
### Access Tokens
34+
The [access tokens](https://openid.net/specs/openid-connect-core-1_0.html#AccessTokenD) are credentials used to access protected resources. If an access token was received from the IdP then it is also stored in the key-value store. To access protected backend, bearer token can be used before calling `proxy_pass` directive as the following example:
35+
36+
```nginx
37+
# Bearer token is uses to authorize NGINX to access protected backend
38+
proxy_set_header Authorization "Bearer $access_token";
39+
40+
# Intercept and redirect "401 Unauthorized" proxied responses to nginx
41+
# for processing with the error_page directive. Necessary if Access Token
42+
# can expire before ID Token.
43+
proxy_intercept_errors on;
44+
45+
proxy_pass http://my_backend; # The backend site/app
46+
```
47+
3348
### Refresh Tokens
3449

3550
If a [refresh token](https://openid.net/specs/openid-connect-core-1_0.html#RefreshTokens) was received from the IdP then it is also stored in the key-value store. When validation of the ID Token fails (typically upon expiry) then NGINX Plus sends the refresh token to the IdP. If the user's session is still valid at the IdP then a new ID token is received, validated, and updated in the key-value store. The refresh process is seamless to the client.
@@ -114,6 +129,8 @@ Manual configuration involves reviewing the following files so that they match y
114129
* Configure the preferred listen port and [enable SSL/TLS configuration](https://docs.nginx.com/nginx/admin-guide/security-controls/terminating-ssl-http/)
115130
* Modify the severity level of the `error_log` directive to suit the deployment environment
116131
* Comment/uncomment the `auth_jwt_key_file` or `auth_jwt_key_request` directives based on whether `$oidc_jwt_keyfile` is a file or URI, respectively
132+
* Uncomment the `proxy_set_header Authorization "Bearer $access_token"` directive if you want to pass access/bearer token in HTTP header to the protected backend/upstream
133+
* Uncoment the `proxy_intercept_errors on` directive if the access token lifetime is less than the ID token lifetime
117134

118135
* **openid_connect.server_conf** - this is the NGINX configuration for handling the various stages of OpenID Connect authorization code flow
119136
* No changes are usually required here
@@ -128,8 +145,9 @@ Manual configuration involves reviewing the following files so that they match y
128145
The key-value store is used to maintain persistent storage for ID tokens and refresh tokens. The default configuration should be reviewed so that it suits the environment. This is part of the advanced configuration in **openid_connect_configuration.conf**.
129146

130147
```nginx
131-
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
132-
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
148+
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
149+
keyval_zone zone=oidc_access_tokens:1M state=conf.d/oidc_access_tokens.json timeout=1h;
150+
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
133151
keyval_zone zone=oidc_pkce:128K timeout=90s;
134152
```
135153

@@ -157,13 +175,15 @@ To delete a single session:
157175

158176
```shell
159177
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/6/http/keyvals/oidc_id_tokens
178+
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/6/http/keyvals/oidc_access_tokens
160179
$ curl -iX PATCH -d '{"<session ID>":null}' localhost:8010/api/6/http/keyvals/refresh_tokens
161180
```
162181

163182
To delete all sessions:
164183

165184
```shell
166185
$ curl -iX DELETE localhost:8010/api/6/http/keyvals/oidc_id_tokens
186+
$ curl -iX DELETE localhost:8010/api/6/http/keyvals/oidc_access_tokens
167187
$ curl -iX DELETE localhost:8010/api/6/http/keyvals/refresh_tokens
168188
```
169189

@@ -223,6 +243,14 @@ proxy_set_header Host <IdP hostname>;
223243
proxy_ssl_name <IdP hostname>;
224244
```
225245

246+
* **Invalid access token**
247+
* The looping can now happen if the access token is not valid for some reason such that the setup of ID/access tokens expiration is different in the IdP.
248+
* In case of this, the backend/upstream can respond with HTTP 401 "Invalid token", and the frontend can restart the flow of OpenID Connect to get new ID token or refresh tokens by calling the following directives.
249+
```nginx
250+
auth_jwt "" token=$session_jwt;
251+
error_page 401 = @do_oidc_flow;
252+
```
253+
226254
## Support
227255

228256
This reference implementation for OpenID Connect is supported for NGINX Plus subscribers.
@@ -236,3 +264,4 @@ This reference implementation for OpenID Connect is supported for NGINX Plus sub
236264
* **R19** Minor bug fixes
237265
* **R22** Separate configuration file, supports multiple IdPs. Configurable scopes and cookie flags. JavaScript is imported as an indepedent module with `js_import`. Container-friendly logging. Additional metrics for OIDC activity.
238266
* **R23** PKCE support. Added support for deployments behind another proxy or load balancer.
267+
* **R28** Access token support. Added support for access token to authorize NGINX to access protected backend.

frontend.conf

+10-1
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,17 @@ server {
2727
# Successfully authenticated users are proxied to the backend,
2828
# with 'sub' claim passed as HTTP header
2929
proxy_set_header username $jwt_claim_sub;
30-
proxy_pass http://my_backend; # The backend site/app
30+
31+
# Bearer token is uses to authorize NGINX to access protected backend
32+
#proxy_set_header Authorization "Bearer $access_token";
3133

34+
# Intercept and redirect "401 Unauthorized" proxied responses to nginx
35+
# for processing with the error_page directive. Necessary if Access Token
36+
# can expire before ID Token.
37+
#proxy_intercept_errors on;
38+
39+
proxy_pass http://my_backend; # The backend site/app
40+
3241
access_log /var/log/nginx/access.log main_jwt;
3342
}
3443
}

openid_connect.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ function auth(r, afterSyncCheck) {
104104
// ID Token is valid, update keyval
105105
r.log("OIDC refresh success, updating id_token for " + r.variables.cookie_auth_token);
106106
r.variables.session_jwt = tokenset.id_token; // Update key-value store
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
}
@@ -255,7 +265,8 @@ function validateIdToken(r) {
255265

256266
function logout(r) {
257267
r.log("OIDC logout for " + r.variables.cookie_auth_token);
258-
r.variables.session_jwt = "-";
268+
r.variables.session_jwt = "-";
269+
r.variables.access_token = '-';
259270
r.variables.refresh_token = "-";
260271
r.return(302, r.variables.oidc_logout_redirect);
261272
}

openid_connect_configuration.conf

+10-7
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,18 @@ map $http_x_forwarded_proto $proto {
8787
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m;
8888

8989
# Change timeout values to at least the validity period of each token type
90-
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
91-
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
90+
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
91+
keyval_zone zone=oidc_access_tokens:1M state=conf.d/oidc_access_tokens.json timeout=1h;
92+
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
9293
keyval_zone zone=oidc_pkce:128K timeout=90s; # Temporary storage for PKCE code verifier.
9394

94-
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
95-
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
96-
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
97-
keyval $request_id $new_refresh zone=refresh_tokens; # ''
98-
keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
95+
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
96+
keyval $cookie_auth_token $access_token zone=oidc_access_tokens; # Exchange cookie for access token
97+
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
98+
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
99+
keyval $request_id $new_access_token zone=oidc_access_tokens;
100+
keyval $request_id $new_refresh zone=refresh_tokens; # ''
101+
keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
99102

100103
auth_jwt_claim_set $jwt_audience aud; # In case aud is an array
101104
js_import oidc from conf.d/openid_connect.js;

0 commit comments

Comments
 (0)