logout
6 TopicsWe Heard You! R35 Brings Frictionless OIDC Logout and Richer Claims to NGINX Plus
Quick Overview Hello friends! NGINX Plus R35 ships four new directives in the built-in ngx_http_oidc_module - logout_uri, post_logout_uri, logout_token_hint, and userinfo. Together, they finally close some of the most common end-to-end OIDC gaps: a clean, standards-aligned RP-initiated logout and easy access to user profile claims. R35 adds a new http_auth_require_module with the auth_require directive so you can implement RBAC checks directly - no more auth_jwt_require + auth_jwt workaround from R34. auth_require isn’t OIDC-specific, you can use it anywhere. In this post, though, we’ll look at it briefly through an OIDC lens. Rather than drowning you in implementation minutiae or some code samples, we’ll walk the actual traffic flow step by step. That way, both admins and engineers can see exactly what’s on the wire and why it matters. What’s new logout_uri - A local path users hit to start logout. NGINX constructs the correct RP-initiated logout request to your IdP, attaching all required parameters for you. post_logout_uri - Where the IdP should send the user after a successful logout. Set it in NGINX and also allow it in your IdP application settings. logout_token_hint on|off - When on, NGINX adds id_token_hint=<JWT> to the IdP’s logout endpoint. Some IdPs (e.g., OneLogin) require this and will return HTTP 400 without it. For most providers, it’s optional. userinfo on|off - When on, NGINX automatically calls userinfo endpoint, fetches extended claims, and exposes them as $oidc_claim_* variables. You can also inspect the raw JSON via $oidc_userinfo. There’s also the new http_auth_require_module with the auth_require directive. It’s not OIDC‑specific, and you can use it anywhere, but in OIDC setups, it’s a straightforward way to implement RBAC directly against $oidc_claim_* claims without reaching for auth_jwt_require. Using OneLogin as the example In a previous article, we used Keycloak as the IdP. This time we’ll try a hosted provider, OneLogin - because it has a few behaviors that make the new features shine. Everything here applies to other providers with the usual small differences. Create an application: OpenID Connect (OIDC) -> Web Application. Sign-in Redirect URIs: https://demo.route443.dev/oidc_callback Sign-out Redirect URIs: https://demo.route443.dev/post_logout/ (Both must match what you configure in NGINX.) On the SSO tab, copy Client ID, Client Secret, and Issuer (typically https://<subdomain>.onelogin.com/oidc/2). On Assignments, grant yourself access, otherwise OneLogin will respond with access_denied after auth. You can refer to our deployment guide for OneLogin. Metadata sanity check Let’s fetch OneLogin’s metadata and note the endpoints that matter for our flow. By default, OneLogin publishes the OpenID Provider Configuration at: https://.onelogin.com/oidc/2/.well-known/openid-configuration curl https://<subdomain>.onelogin.com/oidc/2/.well-known/openid-configuration | jq Example (trimmed): { "issuer": "https://route443-dev.onelogin.com/oidc/2", "authorization_endpoint": "https://route443-dev.onelogin.com/oidc/2/auth", "token_endpoint": "https://route443-dev.onelogin.com/oidc/2/token", "userinfo_endpoint": "https://route443-dev.onelogin.com/oidc/2/me", "end_session_endpoint": "https://route443-dev.onelogin.com/oidc/2/logout" } Two important notes: For RP-initiated logout, NGINX only uses end_session_endpoint from metadata - you can’t override it in the config. If it’s missing, you won’t get a proper RP-initiated logout. If userinfo is on, NGINX will call userinfo_endpoint immediately after exchanging the code for tokens. If userinfo is unavailable, NGINX returns HTTP 500 to the client. Unlike some clients, this is not a soft failure, so if you enable userinfo, make sure that endpoint is up during login. A minimal, working R35 config http { resolver 1.1.1.1 valid=300s ipv4=on; oidc_provider onelogin { issuer https://route443-dev.onelogin.com/oidc/2; client_id 37e2eb90-...; client_secret 4aeca...; logout_uri /logout; post_logout_uri https://demo.route443.dev/post_logout/; logout_token_hint on; userinfo on; } server { listen 443 ssl; server_name demo.route443.dev; ssl_certificate ...; ssl_certificate_key ...; auth_oidc onelogin; proxy_set_header X-Sub $oidc_claim_sub; proxy_set_header X-Userinfo $oidc_userinfo; proxy_pass http://app; location /post_logout/ { return 200 "Arrivederci!\n"; default_type text/plain; } } upstream app { server 127.0.0.1:8080; } } Reload (nginx -s reload) and we’re ready to test. Quick note: on logout_uri the value (/logout) is a trigger, not a location you need to implement yourself. If your app exposes a “Sign out, %username%” link like /logout?user=foo, hitting that URL causes NGINX to perform RP-initiated logout against the IdP. Alternatively, your app can render a different link that points to wherever you’ve configured logout_uri. The key idea is: your app points to the local logout_uri and NGINX handles the IdP call. What userinfo on Does Under the Hood Open your app in a fresh browser session. On the first request, NGINX sees you’re unauthenticated (based on the session cookie) and redirects you to OneLogin. After the authorization code flow, we will now move to the Exchange Code -> Tokens step. We covered this process in detail in a previous article. Because userinfo on is enabled right after NGINX obtains and validates the token set, it calls the userinfo_endpoint from the metadata: NGINX -> OneLogin GET /oidc/2/me HTTP/1.1 Host: route443-dev.onelogin.com Connection: close Authorization: Bearer <access_token> OneLogin -> NGINX HTTP/1.1 200 OK Content-Type: application/json ... {"sub":"177988316","email":"user4@route443.dev","preferred_username":"user4","name":"user4"} NGINX parses the JSON and merges these claims into $oidc_claim_* . Userinfo claims override same-named claims from the id_token. In this example, $oidc_claim_email becomes user4@route443.dev. For troubleshooting, you can inspect the raw body via $oidc_userinfo variable. RP-Initiated Logout Alright, after authentication and authorization have successfully passed and the user has gained access to the application, let’s try signing out. To do this, we’ll open the following link in the browser: https://demo.route443.dev/logout?user=user4. Based on the logout_uri directive, NGINX will understand that it needs to initiate an RP-initiated logout and will redirect the user to the OneLogin provider’s end_session_endpoint, that is, to https://route443-dev.onelogin.com/oidc/2/logout, which we obtained from the metadata. At the same time, NGINX will add the id_token_hint parameter to the request, which contains the user’s ID token that we previously obtained. So the request will look like this: User Agent -> NGINX: HTTP GET /logout?user=user4 Host: demo.route443.dev Cookie: NGX_OIDC_SESSION=ae00b3f...; NGINX -> User Agent: HTTP/1.1 302 Found Location: https://route443-dev.onelogin.com/oidc/2/logout?client_id=37e2eb90...&id_token_hint=ey...&post_logout_redirect_uri=https://demo.route443.dev/post_logout/ Set-Cookie: NGX_OIDC_SESSION=; httponly; secure; path=/ Look closely at the redirect NGINX issues when you hit your local logout_uri. You’ll see it added id_token_hint=<JWT> before sending the browser to the IdP’s end_session_endpoint. That happens because you enabled logout_token_hint on in your NGINX config. With OneLogin this isn’t optional: omit the hint and you’ll be greeted by HTTP 400. With most other providers, the hint is optional, which is exactly why we don’t recommend turning it on unless your IdP demands it. This is the only request where NGINX puts the ID token on the wire in clear view of the user agent and intermediaries, so if you don’t need to expose it, don’t. There’s also UX nuance here. Some IdPs change behavior depending on whether id_token_hint is present. With the hint, you might see an explicit "Are you sure you want to sign out?" confirmation. Without it, the same provider might tear down the session immediately. Same endpoint, different feel. Know what your IdP does and choose intentionally. You’ll notice another parameter NGINX appends: post_logout_redirect_uri. That’s the return address after a successful logout - it must match what you configured in NGINX and what you allowed in the IdP app. In our example, it’s https://demo.route443.dev/post_logout/, which is exactly where the browser lands after OneLogin is done. Now, about the cookie that mysteriously vanishes. NGINX clears NGX_OIDC_SESSION right away. It does this defensively because it cannot predict what the IdP will do next - bounce you to a login page, show a confirmation screen, or even fail. Clearing the local session guarantees you won’t keep accidental access if the IdP misbehaves. Why does that matter? Imagine the provider fails to fully drop the server-side session state. On your next request back to the app, NGINX will dutifully send you to the IdP, the IdP will happily say “oh, you’re still good,” and you’ll be right back in the app with no fresh authentication. That’s not the logout story you want. The takeaway is simple: test RP-initiated logout meticulously with your provider, verify that the server-side session is killed, and only then call it done. In our happy-path run, the flow is pleasantly uneventful: NGINX redirects with id_token_hint (because OneLogin requires it) and post_logout_redirect_uri, OneLogin terminates the session, sends the browser back to /post_logout/, and the user gets their minimalist "Arrivederci!" confirmation. Clean in, clean out: User Agent -> OneLogin: HTTP GET /oidc/2/logout?client_id=37e2eb90...&id_token_hint=ey...&post_logout_redirect_uri=https://demo.route443.dev/post_logout/ Host: route443-dev.onelogin.com OneLogin -> User Agent: HTTP 302 Moved Temporarily Location: https://demo.route443.dev/post_logout/ Declarative RBAC OIDC in NGINX isn’t only about passing identity claims upstream for SSO, it’s also about using those claims to control who gets to which resource. In R34, we had to lean on auth_jwt_require with a little dance:: after a user authenticated, we’d re-feed the ID token to the auth_jwt module just so we could gate access on claims. It worked, but it added config noise, extra $jwt_* variables, and CPU (parsing the token on every request). R35 finally removes that crutch. The new auth_require directive lets us use OIDC claims directly in NGINX, no more "auth_jwt" workaround. The module itself isn’t tied to OIDC, you can pair it with any NGINX config, but with auth_oidc it gives you clean, declarative RBAC right in your config. We’ll keep it practical: imagine two areas in your app, /admin and /support. Admins should access /admin location, admins or folks with the support permission should see /support location. Here’s what that looks like in NGINX. map $oidc_claim_groups $is_admin { default 0; ~*(^|\s)admin(\s|$) 1; } map $oidc_claim_email $is_corp_user { default 0; ~*@example\.com$ 1; } # OR logic map "$is_admin$is_corp_user" $admin_or_corp { default 0; ~1 1; } server { # ... location /admin/ { auth_require $is_admin; proxy_pass http://app; } location /support/ { auth_require $admin_or_corp; proxy_pass http://app; } } In this example we keep things simple: $is_admin comes from the groups claim (we treat it as a space-delimited string) and $is_corp_user checks that the user’s email ends with @example.com. We then build a tiny OR with another map: if either flag is 1, $admin_or_corp becomes 1. From there, auth_require is straightforward - it allows access when the referenced variable is non-empty and not "0" and denies with HTTP 403 by default (you can override the status with error=<4xx|5xx>). Remember that listing multiple variables in a single auth_require is a logical AND, for OR, precompute a boolean with map as shown above. Wrap-Up OIDC improvements in R35 are a meaningful milestone for the module. NGINX Plus R35 lifts the OIDC client from "almost there" to nearly complete: a reliable RP‑initiated logout, first‑class userinfo integration, and a new auth_require for clean, declarative RBAC right in your configuration. We’re not done, though: we still plan to fill a few remaining gaps: Front‑Channel and Back‑Channel logouts, PKCE, and a handful of other niceties that should cover nearly all deployment requirements. Stay tuned!255Views1like0CommentsSAML SLO
Hi I am configuring F5 as a local SP bound to a Idp connector to an external SAML service and I am trying to figure out the logout and why it is not working. I get that uri "saml/sp/profile/post/sls" as part of the exported metadata for the local sp and the redirections are working fine (doing a POST as well) but it doesn't seem like this url is there, i keep getting an error connecting to the backend. Any ideas? F5 11.6 Virtual instance (test environment) with APM. We have two instances, one is in the DMZ for routing and the other one that is internal has the APM module and all the configuration for SAML814Views0likes5CommentsStorefront logout and re-authenticate with no prompt for credentials
Hi, We've integrated citrix storefront with F5 (11.6.2) recently by using iApp . Everything works great but we have an issue with the authentication to the storefront once user logs off from the citrix, Users are able to logon without prompting for username and password when clicked on logon. We are using Imprivata for Radius and its MFA. Any help would be much appreciated. FYI: no user sessions should be terminated after logout is enabled.379Views0likes0CommentsLogout URI in APM seems not work
Hi, I'm using LT+APM mode to authenticate users on a web application. The url to connect on application is https://toto.com/. Once is connected to application he is able to deconnect with a logout button. I configured on my access policie a logout URI: /logout.jsp. When the users click on the logout, he is redirected to the correct page https://toto.com/logout.jsp After a times he tries to connect again to the web app https://toto.com, but the APM doesn't ask again a the client credentials. Normally, after the logout.jsp, the APM must delete session, but it seems not. Any idea about this ? Beb494Views0likes4CommentsDoes BIG IP v11.5.3 support IDP SLO requests through REDIRECT or only POST?
Hopefully a easy question: in v12 the IDP supports SP requests for logout through Redirect and Post. I see this in the idP metdata 'SingleLogoutService ResponseLocation='options for HTTP-POST & HTTP-Redirect are present. We also support BIG IP v11.5.3, in the idP metadata and no HTTP-Redirect. Can we configure this somewhere in later versions (HTTP-Redirect) or is this only available in later releases? Unfortunately the attached SP only supports REDIRECT and SOAP. Before going down another rabbit hole of making changes on the SP I'm hoping there is a quick solution on the F5 (besides updating versions in the immediate term, but well overdue). Appreciate any help, as I'm still really new to BIG IP. Thanks in advance.284Views0likes0CommentsCitrix iApp and logout scenarios
We are currently front ending our Citrix xenapp farm with the BigIP using the citrix iApp. Using our old citrix web gateways to authenticate to citrix, our users were able to log out of the web gateway interfaces and continue working in the published app or full desktop they had loaded without being disconnected. Our new implementation using the BigIP disconnects the user from both the portal page and the current citrix session when the user logs out of the portal. Is there a way to maintain the citrix published app/full desktop connection even after a logout or timeout on the initial portal page? In addition, if a user closes their browser while logged into the portal page and their session is still active, is there a way to force the user back to that session without logging them out of their current session and establishing a new one (ultimately requiring them to re login)? Thanks in advance, this is my first F5 implementation. -GR404Views0likes3Comments