Skip to content

help request: OIDC Login Loop & Refresh Token Failures with openid-connect Plugin for SPA + API Gateway Pattern #12495

Description

@RegMTS

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)

Metadata

Metadata

Labels

pluginquestionlabel for questions asked by userswait for updatewait for the author's response in this issue/PR

Type

No type

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions