SurePassID Identity Provider
SurePassID Identity Provider for NGINX MFA Guide
Applies to: SurePassID Identity Provider 2026.4 Audience: Administrators protecting a web application with NGINX Plus and SurePassID MFA Protocol: OpenID Connect (Authorization Code flow with PKCE)
Overview
This guide configures NGINX Plus as an OpenID Connect relying party (reverse proxy) in front of a backend application. Unauthenticated users are redirected to the SurePassID Identity Provider, complete primary authentication and MFA, and are then proxied to the backend with their identity passed as an HTTP header.
Browser ──► NGINX Plus (OIDC relying party) ──► Backend application
│
└──► SurePassID Identity Provider (/oidc/{domain}/...)
authorize → MFA → callback → token → userinfo
Important change from earlier releases: The Identity Provider uses tenant-scoped endpoints under
/oidc/{domain}/, not the IdentityServer4-style/connect/*paths. PKCE (S256) is required by default. OIDC clients are registered in the Admin Portal, not as a JSON client object.
Requirements
- NGINX Plus (the
auth_jwtandkeyvalfeatures are Plus-only) - The nginx-openid-connect reference implementation
- SurePassID Identity Provider 2026.4 with OIDC enabled for the tenant
- A registered OIDC client for NGINX (see below)
1. Install
Follow the nginx-openid-connect installation instructions, then
install the jq command-line JSON processor. It is a
dependency but is not installed automatically.
sudo yum install jqSELinux workaround
NGINX may lack permission to write to /etc/nginx/conf.d.
The simplest workaround is to change the owner and group of that
directory to nginx:
sudo chown nginx:nginx /etc/nginx/conf.dA better solution is to fix the SELinux httpd_t context
so the NGINX process can create files in that directory. See Using
NGINX Plus with SELinux.
2. Register the OIDC Client in the Admin Portal
Client registration is performed in the Admin Portal and stored in the tenant database. There is no JSON client file to deploy.
Create a new OIDC client with these settings:
| Setting | Value for NGINX |
|---|---|
| Client ID | nginx-client-id (your choice; unique per tenant) |
| Client Name | Display name shown on the consent screen |
| Client Secret | Generate a strong secret; stored salted-hashed and shown only once |
| Redirect URIs | https://app-proxy.example.com/_codexch |
| Post Logout Redirect URIs | https://app-proxy.example.com/_logout |
| Allowed Grant Types | authorization_code refresh_token |
| Allowed Scopes | openid profile email offline_access |
| Require PKCE | Enabled (default) |
| Require Consent | Optional; disable for a seamless proxy experience |
Notes:
- Redirect URIs are matched exactly. No wildcards. The URI must match what NGINX sends, including scheme, host, port, and path.
- Include
offline_accessin Allowed Scopes only if you want refresh tokens, which NGINX uses to extend sessions without re-prompting. - Drop
refresh_tokenfrom the grant types if you do not want refresh behavior.
Optional hardening
| Setting | Purpose |
|---|---|
| Absolute Session Lifetime | Caps the refresh chain, forcing periodic re-authentication with MFA |
| IP Whitelist | Restricts token endpoint calls to your NGINX egress addresses (single IPs or CIDR) |
| FAPI Mode | Enforces S256 PKCE and a mandatory nonce |
| Backchannel Logout URI | Server-to-server signed logout token delivery |
| Frontchannel Logout URI | Browser iframe session clearing |
| ACR values / Override Request ACR | Controls the acr claim emitted for single- vs
multi-factor logins |
MFA behavior
MFA methods presented to the user are governed by the
tenant's MFA policy in the Admin Portal, not by client
properties. Earlier versions of this guide used an IdentityServer4
Properties bag (MfaButtons.ALL,
TenantDomain.N, TenantId.N,
TenantKey.N) — those keys are obsolete and are
ignored.
3. Identity Provider Endpoints
All endpoints are tenant-scoped. Replace {domain} with
your tenant domain and oidc.surepassid.com with your IdP
host.
| Endpoint | Path |
|---|---|
| Discovery | https://oidc.surepassid.com/oidc/{domain}/.well-known/openid-configuration |
| Authorization | https://oidc.surepassid.com/oidc/{domain}/authorize |
| Token | https://oidc.surepassid.com/oidc/{domain}/token |
| UserInfo | https://oidc.surepassid.com/oidc/{domain}/userinfo |
| End session (logout) | https://oidc.surepassid.com/oidc/{domain}/logout |
| JWKS | https://oidc.surepassid.com/oidc/{domain}/jwks |
Advertised capabilities:
response_types_supported:codegrant_types_supported:authorization_code,refresh_tokenscopes_supported:openid,profile,email,phone,offline_accessid_token_signing_alg_values_supported:RS256code_challenge_methods_supported:S256token_endpoint_auth_methods_supported:client_secret_post,client_secret_basic,nonebackchannel_logout_supported/frontchannel_logout_supported:true
4. Configure NGINX Plus
Run the configure.sh script using the tenant discovery
URL:
./configure.sh https://oidc.surepassid.com/oidc/{domain}/.well-known/openid-configurationopenid_connect_configuration.conf
Make the following changes.
Enable PKCE. This is required by the IdP:
map $host $oidc_pkce_enable {
default 1;
}
Point the end-session mapping at the tenant logout endpoint:
map $host $oidc_endsession_endpoint {
default https://oidc.surepassid.com/oidc/{domain}/logout;
}
Use the JWKS URL so signing keys stay up to date automatically:
map $host $oidc_jwks_uri {
default https://oidc.surepassid.com/oidc/{domain}/jwks;
}
Set the client ID and secret:
map $host $oidc_client {
default "<OIDC_CLIENT_ID>";
}
map $host $oidc_client_secret {
default "<OIDC_CLIENT_SECRET>";
}
Set the logout redirect to
/oidc_logout, configured in the next section:
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 "/oidc_logout";
}
Complete openid_connect_configuration.conf example (click to expand)
# OpenID Connect configuration
#
# Each map block allows multiple values so that multiple IdPs can be supported,
# the $host variable is used as the default input parameter but can be changed.
#
map $host $oidc_authz_endpoint {
default https://oidc.surepassid.com/oidc/{domain}/authorize;
}
map $host $oidc_token_endpoint {
default https://oidc.surepassid.com/oidc/{domain}/token;
}
map $host $oidc_endsession_endpoint {
default https://oidc.surepassid.com/oidc/{domain}/logout;
}
map $host $oidc_jwks_uri {
default https://oidc.surepassid.com/oidc/{domain}/jwks;
}
map $host $oidc_jwt_keyfile {
default conf.d/idp_jwk.json;
}
map $host $oidc_client {
default "<OIDC_CLIENT_ID>";
}
map $host $oidc_pkce_enable {
default 1; # PKCE (S256) is required by the IdP
}
map $host $oidc_client_secret {
default "<OIDC_CLIENT_SECRET>";
}
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 "/oidc_logout";
}
map $host $oidc_hmac_key {
# This should be unique for every NGINX instance/cluster
default <UNIQUE_GENERATED_OIDC_HMAC_KEY>;
}
map $proto $oidc_cookie_flags {
http "Path=/; SameSite=lax;"; # For HTTP/plaintext testing
https "Path=/; SameSite=lax; HttpOnly; Secure;"; # Production recommendation
}
map $http_x_forwarded_port $redirect_base {
"" $proto://$host:$server_port;
default $proto://$host:$http_x_forwarded_port;
}
map $http_x_forwarded_proto $proto {
"" $scheme;
default $http_x_forwarded_proto;
}
# ADVANCED CONFIGURATION BELOW THIS LINE
# Additional advanced configuration (server context) in openid_connect.server_conf
# JWK Set will be fetched from $oidc_jwks_uri and cached here - ensure writable by nginx user
proxy_cache_path /var/cache/nginx/jwk levels=1 keys_zone=jwk:64k max_size=1m;
# Change timeout values to at least the validity period of each token type
keyval_zone zone=oidc_id_tokens:1M state=conf.d/oidc_id_tokens.json timeout=1h;
keyval_zone zone=refresh_tokens:1M state=conf.d/refresh_tokens.json timeout=8h;
keyval_zone zone=oidc_pkce:128K timeout=90s; # Temporary storage for PKCE code verifier.
keyval $cookie_auth_token $session_jwt zone=oidc_id_tokens; # Exchange cookie for JWT
keyval $cookie_auth_token $refresh_token zone=refresh_tokens; # Exchange cookie for refresh token
keyval $request_id $new_session zone=oidc_id_tokens; # For initial session creation
keyval $request_id $new_refresh zone=refresh_tokens; # ''
keyval $pkce_id $pkce_code_verifier zone=oidc_pkce;
auth_jwt_claim_set $jwt_audience aud; # In case aud is an array
js_import oidc from conf.d/openid_connect.js;
# vim: syntax=nginx
5. Configure the Backend Proxy (frontend.conf)
Upstream servers
upstream backend_app_server {
zone backend_app_server 64k;
# Server Private IP Address
server 10.1.2.3:443;
# DNS
#resolver 8.8.8.8;
#server app-proxy.example.com:443;
}
SSL
See the ngx_http_ssl_module documentation.
server {
#####################
# SSL Configuration #
#####################
server_name app-proxy.example.com;
listen 443 ssl;
ssl_certificate ssl/example.com.crt;
ssl_certificate_key ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
}
Security note: TLS 1.0 and 1.1 are deprecated and should not be enabled. Use TLS 1.2 or later.
Logout location
Add the /oidc_logout location in the server
block, before the / location. The
post_logout_redirect_uri value must be URL-encoded and must
exactly match a Post Logout Redirect URI registered on the client.
server {
location = /oidc_logout {
proxy_ssl_server_name on; # For SNI to the IdP
proxy_pass $oidc_endsession_endpoint?id_token_hint=$arg_token&post_logout_redirect_uri=https%3a%2f%2fapp-proxy.example.com%2f_logout;
}
location / {
# ...
}
}
Reverse proxy to the backend
server {
location / {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
error_page 401 = @do_oidc_flow;
#auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Successfully authenticated users are proxied to the backend,
# with 'sub' claim passed as HTTP header
proxy_set_header username $jwt_claim_sub;
proxy_pass https://backend_app_server; # The backend site/app
proxy_set_header Host app-proxy.example.com;
proxy_cookie_domain app-proxy.example.com $host;
}
}
Complete frontend.conf example (click to expand)
# This is the backend application we are protecting with OpenID Connect
upstream backend_app_server {
zone backend_app_server 64k;
# Private IP
server 10.1.2.3:443;
# DNS
#resolver 8.8.8.8;
#server app-proxy.example.com:443;
}
# Custom log format to include the 'sub' claim in the REMOTE_USER field
log_format main_jwt '$remote_addr - $jwt_claim_sub [$time_local] "$request" $status '
'$body_bytes_sent "$http_referer" "$http_user_agent" "$http_x_forwarded_for"';
#
# The frontend server - reverse proxy with OpenID Connect authentication
#
server {
include conf.d/openid_connect.server_conf; # Authorization code flow and Relying Party processing
error_log /var/log/nginx/error.log debug; # Reduce severity level as required
#####################
# SSL Configuration #
#####################
server_name app-proxy.example.com;
listen 443 ssl;
ssl_certificate ssl/example.com.crt;
ssl_certificate_key ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
location = /oidc_logout {
proxy_ssl_server_name on; # For SNI to the IdP
proxy_pass $oidc_endsession_endpoint?id_token_hint=$arg_token&post_logout_redirect_uri=https%3a%2f%2fapp-proxy.example.com%2f_logout;
}
location / {
# This site is protected with OpenID Connect
auth_jwt "" token=$session_jwt;
error_page 401 = @do_oidc_flow;
#auth_jwt_key_file $oidc_jwt_keyfile; # Enable when using filename
auth_jwt_key_request /_jwks_uri; # Enable when using URL
# Successfully authenticated users are proxied to the backend,
# with 'sub' claim passed as HTTP header
proxy_set_header username $jwt_claim_sub;
proxy_pass https://backend_app_server; # The backend site/app
proxy_set_header Host app-proxy.example.com;
proxy_cookie_domain app-proxy.example.com $host;
}
}
# vim: syntax=nginx
6. Verify
- Confirm the discovery document loads:
curl https://oidc.surepassid.com/oidc/{domain}/.well-known/openid-configuration - Browse to
https://app-proxy.example.com/. You should be redirected to the IdP. - Complete primary authentication and the MFA challenge.
- Confirm you land on the backend application and that it receives the
usernameheader. - Browse to
/oidc_logoutand confirm the session ends and the browser returns to the registered post-logout URI.
Troubleshooting
| Symptom | Likely cause |
|---|---|
invalid_redirect_uri |
The Redirect URI is not registered exactly; check scheme, host, port, path |
invalid_request mentioning PKCE |
$oidc_pkce_enable is 0; the IdP requires
S256 PKCE |
unauthorized_client |
Grant type not allowed on the client, or wrong tenant domain in the URL |
invalid_client at token endpoint |
Wrong client secret, or caller IP blocked by the client IP whitelist |
| 401 loop at the proxy | JWKS not reachable, or /var/cache/nginx/jwk not
writable by nginx |
| Logout does not return to the app | post_logout_redirect_uri not URL-encoded or not
registered |
| User re-prompted for MFA frequently | Absolute Session Lifetime is short, or offline_access
not granted |
| 404 on all OIDC endpoints | Using legacy /connect/* paths instead of
/oidc/{domain}/* |
Related Documents
- SurePassID Identity Provider User Guide
- SurePassID Identity Provider Capabilities
- SurePassID Identity Provider Release Notes
- nginx-openid-connect reference implementation
Document History
| Version | Date | Change |
|---|---|---|
| 2026.4 | September 15, 2026 | Converted from Confluence export to Markdown; updated to the tenant-scoped OIDC implementation, mandatory PKCE, and Admin Portal client registration |
© 2013–2026 SurePassID. All rights reserved. Protected by patents pending. SurePassID, the SurePassID logo and design, and Secure SSO are registered trademarks or trademarks of SurePassID, Corp. in the United States and/or other jurisdictions. All other marks and names mentioned herein may be trademarks of their respective companies.
SurePassID 360 Central Avenue #800 St. Petersburg, FL 33701 USA +1 (888) 200-8144 surepassid.com