Skip to content

Commit 2584375

Browse files
shawndotkimshawnhankim
authored andcommitted
feat: new endpoints and bundle OIDC simulation environment
chore: agenda and getting started for README.md fix: typo fix: link for README.md fix: signout and sample API for README.md fix: comment for post logout and gitignore fix: space to horizontally aligh variables of key/value fix: README.md fix: image of make watch chore: add a node when Keycloak container isn't working feat: advanced example of proxy server chore: fix typo and add a note for two examples for reverse proxy configuration
1 parent fce421d commit 2584375

32 files changed

+2647
-11
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
*.crt
2+
*.key
3+
*.jwt
4+
refresh_tokens.json
5+
oidc*.json
6+
oidc_credentials.conf
7+
.DS_Store
8+
docker/build-context/data

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.PHONY: start ps watch down stop clean
2+
3+
start:
4+
docker-compose up -d
5+
6+
ps:
7+
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Names}}"
8+
9+
watch:
10+
watch 'docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Names}}"'
11+
12+
down:
13+
docker-compose down
14+
15+
stop:
16+
docker-compose down
17+
18+
clean:
19+
docker kill $$(docker ps -q) 2> /dev/null || true
20+
docker system prune -a
21+
docker volume rm $(docker volume ls -qf dangling=true)

README.md

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,23 @@
22

33
Reference implementation of NGINX Plus as relying party for OpenID Connect authentication
44

5+
- [Description](#description)
6+
- [Refresh Tokens](#refresh-tokens)
7+
- [User Information & Login](#user-information--login)
8+
- [Logout](#logout)
9+
- [Multiple IdPs](#multiple-idps)
10+
- [Getting Started](#🏠-getting-started)
11+
- [Step 1. Quick Starting Guide](#step-1-quick-starting-guide)
12+
- [Step 2. Detailed Starting Guide](#step-2-detailed-starting-guide)
13+
- [Installation](#installation)
14+
- [Configuring your IdP](#configuring-your-idp)
15+
- [Configuring NGINX Plus](#configuring-nginx-plus)
16+
- [Session Management](#session-management)
17+
- [Real time monitoring](#real-time-monitoring)
18+
- [Troubleshooting](#troubleshooting)
19+
- [Support](#support)
20+
- [Changelog](#changelog)
21+
522
## Description
623

724
This repository describes how to enable OpenID Connect integration for [NGINX Plus](https://www.nginx.com/products/nginx/). The solution depends on NGINX Plus components ([auth_jwt module](http://nginx.org/en/docs/http/ngx_http_auth_jwt_module.html) and [key-value store](http://nginx.org/en/docs/http/ngx_http_keyval_module.html)) and as such is not suitable for [open source NGINX](http://www.nginx.org/en).
@@ -32,18 +49,45 @@ For more information on OpenID Connect and JWT validation with NGINX Plus, see [
3249

3350
### Refresh Tokens
3451

35-
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.
52+
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 new ID token and access token are received, validated, and updated in the key-value store. The refresh process is seamless to the client. The ID token is used for user authentication with login and logout, and the access is used for API authorization before proxing to IdP endpoint such as `/userinfo` or custom backend APIs.
53+
54+
### User Information & Login
55+
56+
The `/userinfo` endpoint is used for the following purposes by validating access token via your IdP(s):
57+
58+
- When connecting a frontend app for the first time, the landing page can be shown with `Login` button without user information.
59+
- The frontend app periodically calls the `/userinfo` endpoint, and it returns the status code of `401` as the access token is invalid before clicking `Login` button.
60+
- When an End-User clicks `Login` button, the frontend app calls `/login` endpoint, and the ID token, access token, and refresh token are issued by the IdP and stored in the key-value store of NGINX Plus.
61+
- NGINX Plus successfully redirects to the frontend landing page with session cookie after successful login.
62+
- In the meantime, the user information is shown in the frontend app as the `userinfo` endpoint returns the user data with the status code of `200` by validating access token with the IdP.
3663

3764
### Logout
3865

3966
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.
4067

68+
To avoid breaking changes of API endpoints to customers, the `/v2/logout` location is added to interact with the IdP's `end_session_endpoint` which is to handle [OIDC RP-Initiated Logout](https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout) as the spec of OpenID Connect. The `$post_logout_return_uri` is the URI to which the RP is requesting that the End-User's User Agent be redirected after a logout has been performed. When setting up an IdP, `/v1/_logout` is used in this example, and you can change it per your preference.
69+
4170
### Multiple IdPs
4271

4372
Where NGINX Plus is configured to proxy requests for multiple websites or applications, or user groups, these may require authentication by different IdPs. Separate IdPs can be configured, with each one matching on an attribute of the HTTP request, e.g. hostname or part of the URI path.
4473

4574
> **Note:** When validating OpenID Connect tokens, NGINX Plus can be configured to read the signing key (JWKS) from disk, or a URL. When using multiple IdPs, each one must be configured to use the same method. It is not possible to use a mix of both disk and URLs for the `map…$oidc_jwt_keyfile` variable.
4675
76+
## 🏠 Getting Started
77+
78+
Start by following [step 1](#step-1-quick-starting-guide) if you want to quickly learn and test NGINX Plus OIDC locally. Otherwise skip step 1, and follow the [step 2](#step-2-detailed-starting-guide).
79+
80+
### Step 1. Quick Starting Guide
81+
82+
- 📚 [**How To Set Up and Locally Test NGINX Plus OIDC**](./docs/01-oidc-local-test.md)
83+
84+
### Step 2. Detailed Starting Guide
85+
86+
- [Installation](#installation)
87+
- [Configuring your IdP](#configuring-your-idp)
88+
- [Configuring NGINX Plus](#configuring-nginx-plus)
89+
- [Real time monitoring](#real-time-monitoring)
90+
4791
## Installation
4892

4993
Start by [installing NGINX Plus](https://docs.nginx.com/nginx/admin-guide/installing-nginx/installing-nginx-plus/). In addition, the [NGINX JavaScript module](https://www.nginx.com/blog/introduction-nginscript/) (njs) is required for handling the interaction between NGINX Plus and the OpenID Connect provider (IdP). Install the njs module after installing NGINX Plus by running one of the following:
@@ -100,6 +144,8 @@ When NGINX Plus is deployed behind another proxy, the original protocol and port
100144
- Obtain the URL for `jwks_uri` or download the JWK file to your NGINX Plus instance
101145
- Obtain the URL for the **authorization endpoint**
102146
- Obtain the URL for the **token endpoint**
147+
- Obtain the URL for the **end session endpoint**
148+
- Obtain the URL for the **user info endpoint**
103149

104150
## Configuring NGINX Plus
105151

@@ -110,7 +156,8 @@ Manual configuration involves reviewing the following files so that they match y
110156
- **openid_connect_configuration.conf** - this contains the primary configuration for one or more IdPs in `map{}` blocks
111157

112158
- Modify all of the `map…$oidc_` blocks to match your IdP configuration
113-
- Modify the URI defined in `map…$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location
159+
- Modify the URI defined in `map…$oidc_logout_redirect` to specify an unprotected resource to be displayed after requesting the `/logout` location for customers who has been using R28.
160+
- Modify the URI defined in `map…$oidc_logout_redirect_uri` to specify an unprotected resource to be displayed after requesting the `/v2/_logout` location for customers who start using R29 and wants to change from `map…$oidc_logout_redirect` to `map…$oidc_logout_redirect_uri` to use the feature of `OIDC RP-Initiated Logout` which is interact with IdP's `end_session_endpoint`.
114161
- Set a unique value for `$oidc_hmac_key` to ensure nonce values are unpredictable
115162
- 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.
116163

@@ -121,6 +168,19 @@ Manual configuration involves reviewing the following files so that they match y
121168
- Modify the severity level of the `error_log` directive to suit the deployment environment
122169
- 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
123170

171+
> Note: Two configuration examples are provided as follows.
172+
>
173+
> - 1. Basic Example. Landing page starts OIDC flow without a login button
174+
>
175+
> - 2. Advanced Example. Landing page, login/logout button to start/finish OIDC workflow
176+
> - Landing page with `login` button
177+
> - `login` button to start OIDC flow by validating `id token` with the JWK of IdP.
178+
> - Landing page calls the `/userinfo` endpoint to show user information by validating `access token` with the JWK of IdP.
179+
> - `logout` button to close the OIDC session among frontend, NGINX Plus, and IdP.
180+
> - The proxied API authorization by validating `access token` with the JWK of IdP.
181+
> - Use `access token` for most of IdPs such as Amazon Cognito, Auth0, Keycloak, Okta, OneLogin and Ping Identity.
182+
> - Use `session_jwt` for Azure AD as for now.
183+
124184
- **openid_connect.server_conf** - this is the NGINX configuration for handling the various stages of OpenID Connect authorization code flow
125185

126186
- No changes are usually required here

docker-compose.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
version: '3.4'
2+
3+
networks:
4+
mynetwork:
5+
name: mynetwork
6+
attachable: true
7+
8+
services:
9+
10+
postgres:
11+
container_name: idp-keycloak-db
12+
image: postgres:12.0
13+
# volumes:
14+
# - type: bind
15+
# source: ./docker/build-context/data
16+
# target: /var/lib/postgresql/data
17+
environment:
18+
POSTGRES_DB: keycloak
19+
POSTGRES_USER: keycloak
20+
POSTGRES_PASSWORD: password
21+
ports:
22+
- 5432:5432
23+
networks:
24+
- mynetwork
25+
26+
keycloak:
27+
container_name: idp-keycloak
28+
image: jboss/keycloak:15.1.0
29+
environment:
30+
DB_VENDOR: POSTGRES
31+
DB_ADDR: postgres
32+
DB_DATABASE: keycloak
33+
DB_USER: keycloak
34+
DB_SCHEMA: public
35+
DB_PASSWORD: password
36+
KEYCLOAK_USER: admin
37+
KEYCLOAK_PASSWORD: password
38+
# Uncomment the line below if you want to specify JDBC parameters. The parameter below is just an example, and it shouldn't be used in production without knowledge. It is highly recommended that you read the PostgreSQL JDBC driver documentation in order to use it.
39+
#JDBC_PARAMS: "ssl=true"
40+
ports:
41+
- 8080:8080
42+
depends_on:
43+
- postgres
44+
networks:
45+
- mynetwork
46+
47+
nginxplus_oidc_keycloak_ubuntu18.04:
48+
container_name: nginxplus-oidc-keycloak
49+
build:
50+
context: ./
51+
dockerfile: ./docker/docker-files/nginxplus-ubuntu18.04/Dockerfile
52+
image: nginxplus_oidc_keycloak_ubuntu18.04
53+
ports:
54+
- 8010:8010 # Frontend/backend example v1: landing page w/ OIDC flow w/o login button
55+
- 8020:8020 # Frontend/backend example v2: landing page w/ login button, login button w/ OIDC flow, logout button, /userinfo, access token based API authorization
56+
volumes:
57+
- type: bind
58+
source: ./
59+
target: /etc/nginx/conf.d/
60+
- type: bind
61+
source: ./docker/build-context/nginx/sample/
62+
target: /etc/nginx/sample/
63+
- type: bind
64+
source: ./docker/build-context/content
65+
target: /usr/share/nginx/html/
66+
depends_on:
67+
- keycloak
68+
networks:
69+
- mynetwork

docker/build-context/content/50x.html

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<title>Error</title>
5+
<style>
6+
body {
7+
width: 35em;
8+
margin: 0 auto;
9+
font-family: Tahoma, Verdana, Arial, sans-serif;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<h1>An error occurred.</h1>
15+
<p>Sorry, the page you are looking for is currently unavailable.<br/>
16+
Please try again later.</p>
17+
<p>If you are the system administrator of this resource then you should check
18+
the error log for details.</p>
19+
<p><em>Faithfully yours, nginx.</em></p>
20+
</body>
21+
</html>

0 commit comments

Comments
 (0)