Move Application SSO to the Gateway
Many applications implement single sign-on (SSO) in their own code: they redirect users to the identity provider, validate the tokens that come back, and check the user's roles before serving a request. This guide shows how to move that work to the application's Ingress Gateway, so that the application receives only requests from users who have already logged in.
The examples use Microsoft Entra ID as the identity provider, and a payments application published at https://payments.example.com. The same steps apply to any OpenID Connect (OIDC) provider; only the provider details change. For the general OIDC configuration, see User Authentication with OIDC.
What changes for the application
| Task | Before | After |
|---|---|---|
| Redirect users to log in | Application code | Ingress Gateway |
| Validate tokens | Application code or library | Ingress Gateway |
| Check roles, per path | Application code | Ingress Gateway, with authorization rules |
| Know who the user is | Application session | The X-ID-TOKEN header that the gateway adds to each request |
| Change identity provider | Code change in each application | Change to the Gateway resource |
The application keeps any checks that depend on its own data, such as "this user can only see their own account".
Migration path
Move one application at a time. Each phase can be rolled back on its own.
- Onboard the application: Publish the application through a TSB Ingress Gateway, with TLS, and leave its SSO code unchanged.
- Move login to the gateway: Configure OIDC on the
Gatewayresource (Steps 1 to 3), and remove the login and token validation code from the application (Step 4). Deploy both changes together, so that users are not asked to log in twice. - Move role checks to the gateway (optional): Add authorization rules based on the user's roles (Step 5), and remove the equivalent checks from the application.
Before you begin
- The application is published through a TSB Ingress Gateway over HTTPS. The OIDC flow relies on redirects and cookies, so it must use HTTPS.
- You can create a Kubernetes secret in the namespace of the Ingress Gateway.
- You can create an app registration in your Entra ID tenant, or ask your identity team to create one.
Step 1: Register the application in Entra ID
In the Entra admin center, create an App registration for the application, and note the following values:
| Value | Where to find it | Example |
|---|---|---|
| Tenant ID | Overview page | <tenant-id> |
| Client ID | Overview page, Application (client) ID | <client-id> |
| Client secret | Certificates & secrets → New client secret | <client-secret> |
| Redirect URI | Authentication → Add a platform → Web | https://payments.example.com/callback |
The redirect URI must match the redirectUri in the Gateway resource exactly. Choose a path that the application does not use, such as /callback.
If you plan to check roles at the gateway, also define the roles under App roles, for example payments-admin, and assign them to users or groups under Enterprise applications → Users and groups. Entra ID adds the user's app roles to the roles claim of the ID token.
Step 2: Create the client secret
Create a secret with the client secret, in the namespace of the Ingress Gateway. The key must be istio_generic_secret:
kubectl create secret generic payments-oidc-secret \
--from-literal=istio_generic_secret=<client-secret> \
-n payments
Step 3: Configure OIDC on the Gateway
Add the authentication.oidc section to the HTTP server that publishes the application. The provider URLs follow the same pattern for every Entra ID tenant:
apiVersion: gateway.tsb.tetrate.io/v2
kind: Gateway
metadata:
organization: tetrate
tenant: tetrate
workspace: payments-ws
group: payments-gw
name: payments-gateway
spec:
workloadSelector:
namespace: payments
labels:
app: payments-ingress-gw
http:
- name: payments
port: 443
hostname: payments.example.com
tls:
mode: SIMPLE
secretName: payments-cert
authentication:
oidc:
clientId: <client-id>
clientTokenSecret: payments-oidc-secret
redirectUri: https://payments.example.com/callback
authScopes:
- profile
- email
provider:
issuer: https://login.microsoftonline.com/<tenant-id>/v2.0
authorizationEndpoint: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize
tokenEndpoint: https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token
jwksUri: https://login.microsoftonline.com/<tenant-id>/discovery/v2.0/keys
routing:
rules:
- route:
serviceDestination:
host: payments/payments.payments.svc.cluster.local
port: 8080
- The gateway always requests the
openidscope.profileandemailadd the user's name and email address to the ID token. - Users can log out at
/signout. To use another path, setsignoutPath.
For all the OIDC fields, see OIDCConfig.
Step 4: Update the application
When a user who has not logged in sends a request, the gateway redirects them to Entra ID. After login, the gateway validates the ID token, keeps the tokens in cookies, and forwards each request to the application with the following headers:
| Header | Content |
|---|---|
X-ID-TOKEN | The claims of the validated ID token, as base64url-encoded JSON. Use it to read the user's identity, for example oid, name, preferred_username and roles. |
Authorization | Bearer <access-token>, the access token issued by Entra ID |
Remove the login redirect, the callback handling and the token validation from the application, and read the user's identity from the X-ID-TOKEN header instead. The gateway has already validated the token, so the application only needs to decode the header.
With the scopes in this example, Entra ID issues the access token for Microsoft Graph, and your application cannot validate it. If the application needs an access token for its own API, expose an API scope in the app registration, such as api://<client-id>/payments.read, and add it to authScopes.
The application now trusts the identity in the headers that it receives. Make sure that the gateway is the only way to reach the application from outside its Workspace. If your platform uses a deny-all security posture, as recommended in Extend Security Rules, this is already the case; review any security rules that open the application to other Workspaces.
Step 5: Check roles at the gateway
If the application checks roles before serving some paths, you can move these checks to the gateway. Add an authorization section to the same HTTP server:
authorization:
local:
rules:
- name: admins
from:
- jwt:
other:
roles: payments-admin
to:
- paths: ['/admin*']
- name: signed-in-users
to:
- paths: ['/', '/api*', '/static*']
- The rules match the claims of the ID token. The
rolesclaim is a list; the rule matches if the list containspayments-admin. - The rules form an allow list: a request that no rule allows is refused with
403 RBAC: access denied. List every path that signed-in users may reach. - Every request already comes from a signed-in user, because the gateway redirects users who have not logged in before it applies these rules.
From TSB 1.14.4000, rules can also refuse requests with action: DENY, and match claims with when conditions. See Gateway Authorization Rule Actions.
Entra ID can also add the user's groups to the token, as the groups claim, but it identifies groups by their object ID, and it omits the claim for users who belong to many groups. App roles have readable names, and you assign them per application.
Verify the configuration
Send a request without logging in. The gateway should redirect you to Entra ID:
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' https://payments.example.com/
# 302 https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize?client_id=...
Then open https://payments.example.com/ in a browser, log in, and check that:
- The application responds, and receives the
X-ID-TOKENheader. - A user without the
payments-adminrole receives403 RBAC: access deniedon/admin. /signoutlogs the user out.
Troubleshooting
| Symptom | Likely cause |
|---|---|
Entra ID shows error AADSTS50011 | The redirect URI in the app registration does not match redirectUri in the Gateway resource. |
The login succeeds, but the gateway returns an error on /callback | The client secret is wrong, has expired, or is not in the gateway's namespace. |
403 RBAC: access denied for a user who should have access | The path is not listed in any rule, or the user's ID token does not contain the expected role. Decode the X-ID-TOKEN header to see the claims. |
| No redirect to Entra ID | The Gateway resource has not been applied to the gateway. See Gateway troubleshooting. |
For details of each request, follow the gateway's access logs:
kubectl logs -n payments -l app=payments-ingress-gw -f