Skip to content

Commit fbe17f8

Browse files
committed
Evolve oidc logout
1 parent 39334b6 commit fbe17f8

File tree

5 files changed

+45
-23
lines changed

5 files changed

+45
-23
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,13 @@ When NGINX Plus is deployed behind another proxy, the original protocol and port
9797
* Set the **redirect URI** to the address of your NGINX Plus instance (including the port number), with `/_codexch` as the path, e.g. `https://my-nginx.example.com:443/_codexch`
9898
* Ensure NGINX Plus is configured as a confidential client (with a client secret) or a public client (with PKCE S256 enabled)
9999
* Make a note of the `client ID` and `client secret` if set
100+
* Set the **logout redirect URI** to the address of your NGINX Plus instance (including the port number), with `/_logout` as the path, e.g. `https://my-nginx.example.com:443/_logout`
100101

101102
* If your IdP supports OpenID Connect Discovery (usually at the URI `/.well-known/openid-configuration`) then use the `configure.sh` script to complete configuration. In this case you can skip the next section. Otherwise:
102103
* Obtain the URL for `jwks_uri` or download the JWK file to your NGINX Plus instance
103104
* Obtain the URL for the **authorization endpoint**
104105
* Obtain the URL for the **token endpoint**
106+
* Obtain the URL for the **logout endpoint**
105107

106108
## Configuring NGINX Plus
107109

@@ -111,7 +113,7 @@ Manual configuration involves reviewing the following files so that they match y
111113

112114
* **openid_connect_configuration.conf** - this contains the primary configuration for one or more IdPs in `map{}` blocks
113115
* Modify all of the `map…$oidc_` blocks to match your IdP configuration
114-
* Modify the URI defined in `map…$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location
116+
* Modify the URI defined in `map…$redir_post_logout` to specify an unprotected resource to be displayed after requesting the `/logout` location
115117
* Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
116118
* If NGINX Plus is deployed behind another proxy or load balancer, modify the `map…$redirect_base` and `map…$proto` blocks to define how to obtain the original protocol and port number.
117119

@@ -125,7 +127,7 @@ Manual configuration involves reviewing the following files so that they match y
125127

126128
* **openid_connect.server_conf** - this is the NGINX configuration for handling the various stages of OpenID Connect authorization code flow
127129
* No changes are usually required here
128-
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint`
130+
* Modify the `resolver` directive to match a DNS server that is capable of resolving the IdP defined in `$oidc_token_endpoint` and `$oidc_logout_endpoint`
129131
* If using [`auth_jwt_key_request`](http://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html#auth_jwt_key_request) to automatically fetch the JWK file from the IdP then modify the validity period and other caching options to suit your IdP
130132

131133
* **openid_connect.js** - this is the JavaScript code for performing the authorization code exchange and nonce hashing

configure.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ fi
120120
# Build an intermediate configuration file
121121
# File format is: <NGINX variable name><space><IdP value>
122122
#
123-
jq -r '. | "$oidc_authz_endpoint \(.authorization_endpoint)\n$oidc_token_endpoint \(.token_endpoint)\n$oidc_jwks_uri \(.jwks_uri)"' < /tmp/${COMMAND}_$$_json > /tmp/${COMMAND}_$$_conf
123+
jq -r '. | "$oidc_authz_endpoint \(.authorization_endpoint)\n$oidc_token_endpoint \(.token_endpoint)\n$oidc_logout_endpoint \(.logout_endpoint)\n$oidc_jwks_uri \(.jwks_uri)"' < /tmp/${COMMAND}_$$_json > /tmp/${COMMAND}_$$_conf
124124

125125
# Create a random value for HMAC key, adding to the intermediate configuration file
126126
echo "\$oidc_hmac_key `openssl rand -base64 18`" >> /tmp/${COMMAND}_$$_conf
@@ -178,7 +178,7 @@ fi
178178

179179
# Loop through each configuration variable
180180
echo "$COMMAND: NOTICE: Configuring $CONFDIR/openid_connect_configuration.conf"
181-
for OIDC_VAR in \$oidc_authz_endpoint \$oidc_token_endpoint \$oidc_jwt_keyfile \$oidc_hmac_key $CLIENT_ID_VAR $CLIENT_SECRET_VAR $PKCE_ENABLE_VAR; do
181+
for OIDC_VAR in \$oidc_authz_endpoint \$oidc_token_endpoint \$oidc_logout_endpoint \$oidc_jwt_keyfile \$oidc_hmac_key $CLIENT_ID_VAR $CLIENT_SECRET_VAR $PKCE_ENABLE_VAR; do
182182
# Pull the configuration value from the intermediate file
183183
VALUE=`grep "^$OIDC_VAR " /tmp/${COMMAND}_$$_conf | cut -f2 -d' '`
184184
echo -n "$COMMAND: NOTICE: - $OIDC_VAR ..."

openid_connect.js

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
22
* JavaScript functions for providing OpenID Connect with NGINX Plus
3-
*
3+
*
44
* Copyright (C) 2020 Nginx, Inc.
55
*/
66
var newSession = false; // Used by oidcAuth() and validateIdToken()
77

8-
export default {auth, codeExchange, validateIdToken, logout};
8+
export default {auth, codeExchange, validateIdToken, logout, redirectPostLogout};
99

1010
function retryOriginalRequest(r) {
1111
delete r.headersOut["WWW-Authenticate"]; // Remove evidence of original failed auth_jwt
@@ -51,7 +51,7 @@ function auth(r, afterSyncCheck) {
5151
r.return(302, r.variables.oidc_authz_endpoint + getAuthZArgs(r));
5252
return;
5353
}
54-
54+
5555
// Pass the refresh token to the /_refresh location so that it can be
5656
// proxied to the IdP in exchange for a new id_token
5757
r.subrequest("/_refresh", "token=" + r.variables.refresh_token,
@@ -266,10 +266,17 @@ function validateIdToken(r) {
266266

267267
function logout(r) {
268268
r.log("OIDC logout for " + r.variables.cookie_auth_token);
269-
r.variables.session_jwt = "-";
270-
r.variables.access_token = "-";
271-
r.variables.refresh_token = "-";
272-
r.return(302, r.variables.oidc_logout_redirect);
269+
var logoutArgs = "?post_logout_redirect_uri=" + r.variables.redirect_base + r.variables.oidc_logout_redirect + "&id_token_hint=" + r.variables.session_jwt;
270+
271+
r.variables.session_jwt = '-';
272+
r.variables.access_token = '-';
273+
r.variables.refresh_token = '-';
274+
r.return(302, r.variables.oidc_logout_endpoint + logoutArgs);
275+
}
276+
277+
// Redirect URL after logged-out from the IDP.
278+
function redirectPostLogout(r) {
279+
r.return(302, r.variables.redir_post_logout);
273280
}
274281

275282
function getAuthZArgs(r) {
@@ -311,5 +318,5 @@ function idpClientAuth(r) {
311318
return "code=" + r.variables.arg_code + "&code_verifier=" + r.variables.pkce_code_verifier;
312319
} else {
313320
return "code=" + r.variables.arg_code + "&client_secret=" + r.variables.oidc_client_secret;
314-
}
321+
}
315322
}

openid_connect.server_conf

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
location = /_jwks_uri {
1010
internal;
11-
proxy_cache jwk; # Cache the JWK Set recieved from IdP
11+
proxy_cache jwk; # Cache the JWK Set received from IdP
1212
proxy_cache_valid 200 12h; # How long to consider keys "fresh"
1313
proxy_cache_use_stale error timeout updating; # Use old JWK Set if cannot reach IdP
1414
proxy_ssl_server_name on; # For SNI to the IdP
@@ -29,9 +29,9 @@
2929
# This location is called by the IdP after successful authentication
3030
status_zone "OIDC code exchange";
3131
js_content oidc.codeExchange;
32-
error_page 500 502 504 @oidc_error;
32+
error_page 500 502 504 @oidc_error;
3333
}
34-
34+
3535
location = /_token {
3636
# This location is called by oidcCodeExchange(). We use the proxy_ directives
3737
# to construct the OpenID Connect token request, as per:
@@ -67,16 +67,21 @@
6767
}
6868

6969
location = /logout {
70+
# This location is called by UI to handle OIDC logout with IDP
7071
status_zone "OIDC logout";
71-
add_header Set-Cookie "auth_token=; $oidc_cookie_flags"; # Send empty cookie
72-
add_header Set-Cookie "auth_redir=; $oidc_cookie_flags"; # Erase original cookie
7372
js_content oidc.logout;
7473
}
7574

7675
location = /_logout {
77-
# This location is the default value of $oidc_logout_redirect (in case it wasn't configured)
78-
default_type text/plain;
79-
return 200 "Logged out\n";
76+
# This location is a default value of $oidc_logout_redirect called by the
77+
# IDP after closing user session in the IDP.
78+
79+
# Clean cookies
80+
add_header Set-Cookie "auth_nonce=; $oidc_cookie_flags"; # Send empty cookie
81+
add_header Set-Cookie "auth_token=; $oidc_cookie_flags"; # Erase original cookie
82+
add_header Set-Cookie "auth_redir=; $oidc_cookie_flags";
83+
84+
js_content oidc.redirectPostLogout;
8085
}
8186

8287
location @oidc_error {

openid_connect_configuration.conf

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ map $host $oidc_jwt_keyfile {
2828
default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs";
2929
}
3030

31+
map $host $oidc_logout_endpoint {
32+
default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/logout";
33+
}
34+
35+
map $host $redir_post_logout {
36+
# Where to send browser after requesting /logout location. This can be
37+
# replaced with a custom logout page, or complete URL.
38+
default "http://127.0.0.1:8080";
39+
}
40+
3141
map $host $oidc_client {
3242
default "my-client-id";
3343
}
@@ -45,9 +55,7 @@ map $host $oidc_scopes {
4555
}
4656

4757
map $host $oidc_logout_redirect {
48-
# Where to send browser after requesting /logout location. This can be
49-
# replaced with a custom logout page, or complete URL.
50-
default "/_logout"; # Built-in, simple logout page
58+
default "/_logout"; # This is called by IdP after successful logout.
5159
}
5260

5361
map $host $oidc_hmac_key {

0 commit comments

Comments
 (0)