Description
Hello APISIX Team,
I am facing a persistent issue trying to secure a Single-Page Application (SPA) and its backend API using the openid-connect plugin with Keycloak as the IdP. My goal is to have all security managed by the APISIX platform, without any OIDC logic in the frontend application itself.
Architecture:
- APISIX as the API Gateway.
- Keycloak as the OIDC Identity Provider.
- Frontend: A static SPA served by an Nginx container.
- Backend: A REST/OData API served by another container.
- Both frontend and backend need to be protected, as the frontend needs user identity to render correctly.
The Problem:
I cannot find a working configuration for the openid-connect plugin that avoids either
a) token refresh failures, or
b) login loops.
What I Have Tried:
Separate Routes, Separate OIDC Plugins, Unified Keycloak Client:
- Route 1 (/*, for frontend) with openid-connect plugin.
- Route 2 (/api/*, for backend) with an identical openid-connect plugin configuration.
- Result: Login works. Claim propagation works. However, after the access token expires, the token refresh fails. Keycloak logs show the error invalid_grant: "Token client and authorized client don't match". This happens because the refresh is initiated by the plugin instance on the /api/ route, but the token was originally obtained by the plugin instance on the /* route. The context (likely due to different redirect_uri configurations in the plugins) does not match.
Shared Callback Route:
- To solve the context mismatch, I configured both openid-connect plugins to use the exact same redirect_uri (e.g., https://.../auth/callback).
- A dedicated route for /auth/callback handles the redirect back to the application.
- Result: This leads to a login loop. The OIDC flow is never completed because the /auth/callback route does not have the openid-connect plugin to handle the code-for-token exchange.
Unified redirect_uri on Main Route:
- I configured both plugin instances to use the application root (https://.../) as the redirect_uri.
- Result: This causes the openid-connect plugin to fail with a 500 Internal Server Error on the initial visit to /. The logs show unhandled request to the redirect_uri: /. The plugin cannot handle when the application entry point is the same as the configured redirect_uri.
Conclusion:
It seems impossible to have two separate routes protected by the openid-connect plugin in bearer_only: false mode, as the session/refresh context cannot be reliably shared. At the same time, using a single route to protect both also presents challenges.
Is there a recommended, official architecture for this common use case (platform-managed security for an SPA)? Am I missing a configuration option, or is this a limitation of the current plugin?
Thank you for your help.
Environment
- APISIX version (run
apisix version): 3.11.0
- Operating system (run
uname -a): Ubuntu 24.04.2 (kernel 6.8.0-64-generic)
- OpenResty / Nginx version (run
openresty -V or nginx -V): nginx v1 (docker image: nginx:1-alpine)
Description
Hello APISIX Team,
I am facing a persistent issue trying to secure a Single-Page Application (SPA) and its backend API using the openid-connect plugin with Keycloak as the IdP. My goal is to have all security managed by the APISIX platform, without any OIDC logic in the frontend application itself.
Architecture:
The Problem:
I cannot find a working configuration for the openid-connect plugin that avoids either
a) token refresh failures, or
b) login loops.
What I Have Tried:
Separate Routes, Separate OIDC Plugins, Unified Keycloak Client:
Shared Callback Route:
Unified redirect_uri on Main Route:
Conclusion:
It seems impossible to have two separate routes protected by the openid-connect plugin in bearer_only: false mode, as the session/refresh context cannot be reliably shared. At the same time, using a single route to protect both also presents challenges.
Is there a recommended, official architecture for this common use case (platform-managed security for an SPA)? Am I missing a configuration option, or is this a limitation of the current plugin?
Thank you for your help.
Environment
apisix version): 3.11.0uname -a): Ubuntu 24.04.2 (kernel 6.8.0-64-generic)openresty -Vornginx -V): nginx v1 (docker image: nginx:1-alpine)