Skip to content

Commit

Permalink
Add OIDC end session endpoint support
Browse files Browse the repository at this point in the history
  • Loading branch information
shawnhankim committed Jan 9, 2023
1 parent 74948ce commit 6cc94b4
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 13 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ If a [refresh token](https://openid.net/specs/openid-connect-core-1_0.html#Refre

### Logout

Requests made to the `/logout` location invalidate both the ID token, access 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.
Requests made to the `/logout` location invalidate both the ID token and refresh token by erasing them from the key-value store. Next, the User Agent is redirected to the `$oidc_end_session_endpoint` in order to terminate the user session on the IdP's side. After the session is successfully terminated on the IdP side, the User Agent will be redirected to the `$oidc_logout_landing_page`. Note that the `$oidc_logout_landing_page` endpoint must not require authentication, otherwise the user authentication process may be initiated from the beginning.

### Multiple IdPs

Expand Down Expand Up @@ -102,6 +102,7 @@ When NGINX Plus is deployed behind another proxy, the original protocol and port
* Obtain the URL for `jwks_uri` or download the JWK file to your NGINX Plus instance
* Obtain the URL for the **authorization endpoint**
* Obtain the URL for the **token endpoint**
* Obtain the URL for the **end session endpoint**

## Configuring NGINX Plus

Expand All @@ -111,7 +112,7 @@ Manual configuration involves reviewing the following files so that they match y

* **openid_connect_configuration.conf** - this contains the primary configuration for one or more IdPs in `map{}` blocks
* Modify all of the `map…$oidc_` blocks to match your IdP configuration
* Modify the URI defined in `map…$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location
* Modify the URI defined in `map…$oidc_logout_landing_page` to redirect browser after successful logout
* Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
* 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.

Expand Down
4 changes: 2 additions & 2 deletions configure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ fi
# Build an intermediate configuration file
# File format is: <NGINX variable name><space><IdP value>
#
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
jq -r '. | "$oidc_authz_endpoint \(.authorization_endpoint)\n$oidc_token_endpoint \(.token_endpoint)\n$oidc_jwks_uri \(.jwks_uri)\n$oidc_end_session_endpoint \(.end_session_endpoint)"' < /tmp/${COMMAND}_$$_json > /tmp/${COMMAND}_$$_conf

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

# Loop through each configuration variable
echo "$COMMAND: NOTICE: Configuring $CONFDIR/openid_connect_configuration.conf"
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
for OIDC_VAR in \$oidc_authz_endpoint \$oidc_token_endpoint \$oidc_jwt_keyfile \$oidc_end_session_endpoint \$oidc_hmac_key $CLIENT_ID_VAR $CLIENT_SECRET_VAR $PKCE_ENABLE_VAR; do
# Pull the configuration value from the intermediate file
VALUE=`grep "^$OIDC_VAR " /tmp/${COMMAND}_$$_conf | cut -f2 -d' '`
echo -n "$COMMAND: NOTICE: - $OIDC_VAR ..."
Expand Down
18 changes: 16 additions & 2 deletions openid_connect.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
var newSession = false; // Used by oidcAuth() and validateIdToken()

export default {auth, codeExchange, validateIdToken, logout};
export default {auth, codeExchange, validateIdToken, logout, redirectPostLogout};

function retryOriginalRequest(r) {
delete r.headersOut["WWW-Authenticate"]; // Remove evidence of original failed auth_jwt
Expand Down Expand Up @@ -263,12 +263,26 @@ function validateIdToken(r) {
}
}

// Default RP-Initiated or Custom Logout w/ OP as per:
// https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
// https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RedirectionAfterLogout
// An RP requests that the OP log out the end-user by redirecting the end-user's
// User Agent to the OP's Logout endpoint.
function logout(r) {
r.log("OIDC logout for " + r.variables.cookie_auth_token);
var queryParams = '';
if (r.variables.oidc_logout_query_params) {
queryParams = '?' + r.variables.oidc_logout_query_params;
}
r.variables.session_jwt = "-";
r.variables.access_token = "-";
r.variables.refresh_token = "-";
r.return(302, r.variables.oidc_logout_redirect);
r.return(302, r.variables.oidc_end_session_endpoint + queryParams);
}

// Redirect URI after logged-out from the OP.
function redirectPostLogout(r) {
r.return(302, r.variables.oidc_logout_landing_page);
}

function getAuthZArgs(r) {
Expand Down
19 changes: 16 additions & 3 deletions openid_connect.server_conf
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,27 @@
}

location = /logout {
# RP-Initiated Logout to interact with $oidc_end_session_endpoint as per:
# https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout
status_zone "OIDC logout";
add_header Set-Cookie "auth_token=; $oidc_cookie_flags"; # Send empty cookie
add_header Set-Cookie "auth_redir=; $oidc_cookie_flags"; # Erase original cookie
js_content oidc.logout;
}

location = /_logout {
# This location is the default value of $oidc_logout_redirect (in case it wasn't configured)
# This location is a RP's callback URI which is called by the IdP after
# successful logout from the IdP by calling $oidc_logout_endpoint.

# Clean cookies
add_header Set-Cookie "auth_token=; $oidc_cookie_flags"; # Send empty cookie
add_header Set-Cookie "auth_redir=; $oidc_cookie_flags"; # Erase original cookie
add_header Set-Cookie "auth_nonce=; $oidc_cookie_flags";

js_content oidc.redirectPostLogout;
}

location = /logout_page {
# This location is a default value of $oidc_logout_landing_page as a
# Built-in, simple logout page in case it wasn't configured.
default_type text/plain;
return 200 "Logged out\n";
}
Expand Down
20 changes: 16 additions & 4 deletions openid_connect_configuration.conf
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ map $host $oidc_jwt_keyfile {
default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/certs";
}

map $host $oidc_end_session_endpoint {
default "http://127.0.0.1:8080/auth/realms/master/protocol/openid-connect/logout";
}

map $host $oidc_logout_query_params {
# Each IdP may use different query params of the $oidc_end_session_endpoint.
# For example, Amazon Cognito requires 'logout_uri', and Auth0 requires
# 'returnTo' instead of 'post_logout_redirect_uri' in the query params.
default "post_logout_redirect_uri=$redirect_base/_logout&id_token_hint=$session_jwt";;
#www.example.com "client_id=$oidc_client&logout_uri=$redirect_base/_logout";
}

map $host $oidc_client {
default "my-client-id";
}
Expand All @@ -44,10 +56,10 @@ map $host $oidc_scopes {
default "openid+profile+email+offline_access";
}

map $host $oidc_logout_redirect {
# Where to send browser after requesting /logout location. This can be
# replaced with a custom logout page, or complete URL.
default "/_logout"; # Built-in, simple logout page
map $host $oidc_logout_landing_page {
# Where to redirect browser after successful logout from the IdP.
default "$redirect_base/logout_page"; # Built-in, simple logout page
#www.example.com $redirect_base;
}

map $host $oidc_hmac_key {
Expand Down

0 comments on commit 6cc94b4

Please sign in to comment.