Skip to content

Commit 24a8acb

Browse files
committed
feat: added OIDC logout endpoint
1 parent 6ad8ec6 commit 24a8acb

5 files changed

+102
-12
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ If a [refresh token](https://openid.net/specs/openid-connect-core-1_0.html#Refre
3636

3737
### Logout
3838

39-
Requests made to the `/logout` location invalidate both the ID token and refresh token by erasing them from the key-value store. Therefore, subsequent requests to protected resources will be treated as a first-time request and send the client to the IdP for authentication. Note that the IdP may issue cookies such that an authenticated session still exists at the IdP.
39+
Requests made to the `/logout` location invalidate both the ID token and refresh token by erasing them from the key-value store. Therefore, subsequent requests to protected resources will be treated as a first-time request and send the client to the IdP for authentication. By interacting with `$oidc_logout_endpoint` which is the end session endpoint of IdP, the authenticated session is ended at the IdP.
4040

4141
### Multiple IdPs
4242

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_jwks_uri \(.jwks_uri)\n$oidc_logout_endpoint \(.logout_endpoint)"' < /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_jwt_keyfile \$oidc_logout_endpoint \$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: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
*/
66
var newSession = false; // Used by oidcAuth() and validateIdToken()
77

8-
export default {auth, codeExchange, validateIdToken, logout};
8+
const EXTRA_PARAMS = 1;
9+
const REPLACE_PARAMS = 2;
10+
11+
export default {auth, codeExchange, validateIdToken, logout, redirectPostLogout};
912

1013
function retryOriginalRequest(r) {
1114
delete r.headersOut["WWW-Authenticate"]; // Remove evidence of original failed auth_jwt
@@ -253,11 +256,37 @@ function validateIdToken(r) {
253256
}
254257
}
255258

259+
// Default RP-Initiated or Custom Logout w/ OP.
260+
// - An RP requests that the OP log out the end-user by redirecting the
261+
// end-user's User Agent to the OP's Logout endpoint.
262+
// - https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
263+
// - https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RedirectionAfterLogout
256264
function logout(r) {
257265
r.log("OIDC logout for " + r.variables.cookie_auth_token);
258-
r.variables.session_jwt = "-";
259-
r.variables.refresh_token = "-";
260-
r.return(302, r.variables.oidc_logout_redirect);
266+
var idToken = r.variables.session_jwt;
267+
var queryParams = '?post_logout_redirect_uri=' +
268+
r.variables.redirect_base +
269+
r.variables.oidc_logout_redirect +
270+
'&id_token_hint=' + idToken;
271+
if (r.variables.oidc_logout_query_params_option == REPLACE_PARAMS) {
272+
queryParams = '?' + r.variables.oidc_logout_query_params;
273+
} else if (r.variables.oidc_logout_query_params_option == EXTRA_PARAMS) {
274+
queryParams += '&' + r.variables.oidc_logout_query_params;
275+
}
276+
r.variables.request_id = '-';
277+
r.variables.session_jwt = '-';
278+
r.variables.access_token = '-';
279+
r.variables.refresh_token = '-';
280+
r.return(302, r.variables.oidc_logout_endpoint + queryParams);
281+
}
282+
283+
// Redirect URI after logged-out from the OP.
284+
function redirectPostLogout(r) {
285+
if (r.variables.post_logout_return_uri) {
286+
r.return(302, r.variables.post_logout_return_uri);
287+
} else {
288+
r.return(302, r.variables.redirect_base + r.variables.cookie_auth_redir);
289+
}
261290
}
262291

263292
function getAuthZArgs(r) {

openid_connect.server_conf

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,30 @@
6767
}
6868

6969
location = /logout {
70+
# RP-Initiated Logout to interact with $oidc_end_session_endpoint as per:
71+
# https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
7072
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
7373
js_content oidc.logout;
7474
}
7575

7676
location = /_logout {
77-
# This location is the default value of $oidc_logout_redirect (in case it wasn't configured)
77+
# This location is a RP's callback URI that is the default value of
78+
# $oidc_logout_redirect which is called by OP after successful logout.
79+
80+
# Clean cookies
81+
add_header Set-Cookie "auth_token=; $oidc_cookie_flags"; # Send empty cookie
82+
add_header Set-Cookie "auth_redir=; $oidc_cookie_flags"; # Erase original cookie
83+
add_header Set-Cookie "auth_nonce=; $oidc_cookie_flags";
84+
85+
# Enable following directives, and disable the oidc.redirectPostLogout()
86+
# if you want to test a Built-in simple logout page before production.
7887
default_type text/plain;
7988
return 200 "Logged out\n";
89+
90+
# Enable oidc.redirectPostLogout(), and disable the above built-in logout
91+
# page if you want to redirect to either the landing page or custom
92+
# logout page using the map of $post_logout_return_uri.
93+
#js_content oidc.redirectPostLogout;
8094
}
8195

8296
location @oidc_error {

openid_connect_configuration.conf

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,27 @@ 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 $oidc_logout_query_params_option {
36+
# 0: default query params for the RP-initiated logout
37+
# 1: extra query params is added after the default query params
38+
# 2: replace default query params with custom query params
39+
default 0;
40+
}
41+
42+
map $host $oidc_logout_query_params {
43+
# Each IdP may use different query params of the $oidc_logout_endpoint. For
44+
# example, The Amazon Cognito requires `client_id` and `logout_uri`. The
45+
# Auth0 requires `client_id` and `returnTo`. If the map value is empty, then
46+
# `post_logout_redirect_uri` and `id_token_hint` are used as default query
47+
# params for most of IdPs like AzureAD/Okta/Keycloak/OneLogin/PingIdentity.
48+
default "";
49+
#www.example.com "client_id=$oidc_client&logout_uri=$redirect_base/_logout";
50+
}
51+
3152
map $host $oidc_client {
3253
default "my-client-id";
3354
}
@@ -44,12 +65,38 @@ map $host $oidc_scopes {
4465
default "openid+profile+email+offline_access";
4566
}
4667

68+
map $host $oidc_landing_page {
69+
# Where to send browser after successful login. This option is only
70+
# recommended for scenarios where a landing page shows default information
71+
# without login, and the RP redirects to the landing page after successful
72+
# login from the OP. If this is empty, then the RP redirects to $request_uri.
73+
default "";
74+
#www.example.com $redirect_base;
75+
}
76+
4777
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.
78+
# This is a RP's callback URI which is called by OP after successful logout.
5079
default "/_logout"; # Built-in, simple logout page
5180
}
5281

82+
map $host $post_logout_return_uri {
83+
# Where to send browser after the RP requests /logout to the OP, and after
84+
# the RP (/_logout) is called by the OP and cleans cookies. The following
85+
# examples can be replaced with a custom logout page, or a complete URL.
86+
# If this is empty, then the RP redirects to $request_uri.
87+
88+
default "";
89+
90+
# Enable if you want to redirect to the landing page
91+
# www.example.com $oidc_landing_page;
92+
93+
# Enable and edit if you want to redirect to a custom logout page
94+
#www.example.com $redirect_base/signout;
95+
96+
# Enable and edit if you want to redirect to an another complete URL
97+
#www.example.com https://www.nginx.com;
98+
}
99+
53100
map $host $oidc_hmac_key {
54101
# This should be unique for every NGINX instance/cluster
55102
default "ChangeMe";

0 commit comments

Comments
 (0)