Skip to content

feat(openid-connect): add consumer selector for consumer-group realm routing - #13038

Open
PiyushMishra318 wants to merge 2 commits into
apache:masterfrom
PiyushMishra318:feat/openid-connect-consumer-selector
Open

PiyushMishra318 wants to merge 2 commits into
apache:masterfrom
PiyushMishra318:feat/openid-connect-consumer-selector

Conversation

@PiyushMishra318

Copy link
Copy Markdown

This PR adds an optional consumer_selector capability to the openid-connect plugin, allowing a single route to select a Consumer (and Consumer Group) from a JWT claim (for example, iss) before OIDC validation.

With this flow, APISIX can apply realm-specific openid-connect configurations from Consumer Groups deterministically on one route, without requiring key-auth pre-resolution.

Changes included:

  • Added consumer_selector schema fields in openid-connect:
    • enabled
    • claim (default iss)
    • map (claim value -> consumer name)
    • strict
  • Added selector logic to:
    • extract bearer token
    • decode JWT payload claim
    • resolve/attach mapped consumer
    • load selected Consumer Group’s openid-connect config
    • continue normal OIDC validation/header-setting flow (including X-Userinfo)
  • Added consumer.get_consumer(name) helper and safe local-cache cloning in consumer.lua
  • Added tests for selector schema validation in t/plugin/openid-connect2.t
  • Updated plugin documentation in docs/en/latest/plugins/openid-connect.md

Which issue(s) this PR fixes:

Fixes #13037

Checklist

  • I have explained the need for this PR and the problem it solves
  • I have explained the changes or the new features added to this PR
  • I have added tests corresponding to this change
  • I have updated the documentation to reflect this change
  • I have verified that this change is backward compatible (If not, please discuss on the APISIX mailing list first)

@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request plugin labels Feb 26, 2026
@PiyushMishra318

Copy link
Copy Markdown
Author

Thanks in advance for reviewing this PR.

I’m happy to iterate on the implementation based on maintainer feedback, including behavior changes, API/schema adjustments, additional tests, or splitting this into smaller PRs if that is preferred.

@Baoyuantop Baoyuantop left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A better approach is to directly support multiple Issuer configurations (valid_issuers + multiple discovery) within the openid-connect plugin, instead of distributing routes via "unsigned JWT Claims". Consider supporting multiple Issuer configurations directly within the openid-connect plugin (each issuer corresponds to a set of discovery/client_id/client_secret), which is a more secure and concise solution, eliminating the need for routing before signature verification.

@PiyushMishra318
PiyushMishra318 marked this pull request as draft March 5, 2026 06:26

@moonming moonming left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @PiyushMishra318, thank you for your contribution and the effort you put into this!

After reviewing the approach, I have some concerns that I think warrant a different direction:

  1. Core layer modification concern: Adding JWT decoding logic (jwt_iss variable) into core/ctx.lua introduces unsigned JWT parsing at the core routing layer. This could have unintended security implications since it bypasses signature verification.

  2. Better alternative exists: The same goal (routing to different consumers based on JWT issuer) can be achieved using APISIX's existing mechanisms:

    • Multiple routes with vars conditions to match different issuers
    • The built-in consumer and consumer_group mechanism with the existing openid-connect plugin
  3. File scope mismatch: The PR modifies FAQ.md and radixtree-uri-vars tests rather than the openid-connect plugin itself, which suggests the changes may not be targeting the right layer.

Since this is still in draft status, I'd recommend exploring the route-based approach using vars conditions — it would achieve the same result without modifying the core context. Happy to discuss further if you'd like to explore that direction!

@moonming moonming left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @PiyushMishra318, thank you for your contribution and the effort you put into this!

After reviewing the approach, I have some concerns that I think warrant a different direction:

  1. Core layer modification concern: Adding JWT decoding logic into core/ctx.lua introduces unsigned JWT parsing at the core routing layer. This could have unintended security implications since it bypasses signature verification.

  2. Better alternative exists: The same goal (routing to different consumers based on JWT issuer) can be achieved using APISIX existing mechanisms - multiple routes with vars conditions to match different issuers, or the built-in consumer and consumer_group mechanism with the existing openid-connect plugin.

  3. File scope mismatch: The PR modifies FAQ.md and radixtree-uri-vars tests rather than the openid-connect plugin itself, which suggests the changes may not be targeting the right layer.

Since this is still in draft status, I would recommend exploring the route-based approach using vars conditions - it would achieve the same result without modifying the core context. Happy to discuss further if you would like to explore that direction!

@PiyushMishra318

Copy link
Copy Markdown
Author

Hi @PiyushMishra318, thank you for your contribution and the effort you put into this!

After reviewing the approach, I have some concerns that I think warrant a different direction:

  1. Core layer modification concern: Adding JWT decoding logic into core/ctx.lua introduces unsigned JWT parsing at the core routing layer. This could have unintended security implications since it bypasses signature verification.
  2. Better alternative exists: The same goal (routing to different consumers based on JWT issuer) can be achieved using APISIX existing mechanisms - multiple routes with vars conditions to match different issuers, or the built-in consumer and consumer_group mechanism with the existing openid-connect plugin.
  3. File scope mismatch: The PR modifies FAQ.md and radixtree-uri-vars tests rather than the openid-connect plugin itself, which suggests the changes may not be targeting the right layer.

Since this is still in draft status, I would recommend exploring the route-based approach using vars conditions - it would achieve the same result without modifying the core context. Happy to discuss further if you would like to explore that direction!

Thanks for your feedback @moonming.

We did explore the vars-based routing approach using the http-x-tenant header for our use case, but it didn’t fully solve the problem for us. Similarly, with the consumer/consumer_group approach, our understanding is that consumer resolution happens after authentication succeeds, which makes it difficult to dynamically switch auth strategies based on the JWT issuer.

One of the main reasons we considered APISIX was to avoid maintaining a bespoke multi-auth/realm handling system. However, as we scaled to more auth scopes, the configuration complexity grew significantly, which is what led us to experiment with a core-level change.

That said, I completely understand your concern around introducing unsigned JWT parsing at the core layer, especially from a general security standpoint. In our case, since these APIs are primarily internal, we may consider maintaining this as an internal patch instead of pushing it upstream.

I do think native support for this kind of use case in APISIX would be valuable in the future—ideally with a more robust and secure approach than what we proposed here. Happy to discuss or explore alternative directions if there’s a recommended pattern we might have missed.

@Baoyuantop

Copy link
Copy Markdown
Contributor

Thanks for the detailed explanation, @PiyushMishra318. The pain point you described is valid — the current openid-connect plugin assumes a single realm per route, and the consumer resolution timing makes it impossible to dynamically switch OIDC configurations before authentication.

I think the right direction would be to add multi-issuer support directly within the openid-connect plugin itself, rather than at the core routing layer. The plugin could:

  1. Accept a realms map (issuer → discovery/client config set)
  2. Do a lightweight JWT payload decode internally (within the plugin scope) to extract the iss claim
  3. Select the matching realm config and proceed with standard OIDC validation

This keeps the unsigned JWT parsing scoped to the auth plugin rather than the core context, and provides a cleaner API for your use case.

Maintaining an internal patch is totally reasonable for your immediate needs. If you are interested in contributing an upstream solution along these lines, we would welcome a proposal on the issue for further discussion.

@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 4 weeks if no further activity occurs. If you think that's incorrect or this pull request should instead be reviewed, please simply write any comment. Even if closed, you can still revive the PR at any time or discuss it on the dev@apisix.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label May 18, 2026
@PiyushMishra318

Copy link
Copy Markdown
Author

Hi @Baoyuantop

Proposed approach for multi-issuer OIDC routing

Based on your feedback, I've moved all logic out of core/ctx.lua and into the openid-connect plugin itself. No changes to the core routing layer.

What it does

Adds an optional realms array to the plugin config. Each entry maps an OIDC issuer to its own discovery, client_id, and client_secret. On each request, the plugin does a lightweight unsigned base64 decode of the bearer token payload to extract the iss claim, finds the matching realm, and proceeds with standard OIDC validation using that realm's credentials. Unrecognized issuers return a plain 401.

Single-issuer configs are completely unchanged — realms is opt-in.

Example config

plugins:
  openid-connect:
    bearer_only: true
    realms:
      - issuer: "https://idp.example.com/realms/tenantA"
        discovery: "https://idp.example.com/realms/tenantA/.well-known/openid-configuration"
        client_id: "client-a"
        client_secret: "secret-a"
      - issuer: "https://idp.example.com/realms/tenantB"
        discovery: "https://idp.example.com/realms/tenantB/.well-known/openid-configuration"
        client_id: "client-b"
        client_secret: "secret-b"

What changes

  1. apisix/plugins/openid-connect.lua only — schema + logic
  2. Issuer matching is exact string only (no wildcards/regex)
  3. Per-realm config is minimal (discovery, client_id, client_secret) — top-level flags like use_jwks, bearer_only, public_key apply globally. This scope can be expanded in a follow-up if needed.
  4. lua-resty-openidc has no built-in multi-issuer support, so the realm selection happens before the library is called.

Does this approach align with what you had in mind? Happy to adjust before pushing the full implementation.

@github-actions github-actions Bot removed the stale label May 27, 2026
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 828e7c6 to 5bf6036 Compare June 19, 2026 02:55
@PiyushMishra318
PiyushMishra318 marked this pull request as ready for review June 19, 2026 02:56
@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 4 weeks if no further activity occurs. If you think that's incorrect or this pull request should instead be reviewed, please simply write any comment. Even if closed, you can still revive the PR at any time or discuss it on the dev@apisix.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label Aug 18, 2026
@PiyushMishra318
PiyushMishra318 marked this pull request as draft September 3, 2026 06:57
@github-actions github-actions Bot removed the stale label Sep 3, 2026
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch 4 times, most recently from 3b82e64 to 0a75b73 Compare September 14, 2026 09:37
PiyushMishra318 added a commit to PiyushMishra318/apisix that referenced this pull request Sep 14, 2026
…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 0a75b73 to cb75e57 Compare September 14, 2026 09:52
PiyushMishra318 added a commit to PiyushMishra318/apisix that referenced this pull request Sep 14, 2026
…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from cb75e57 to 0928862 Compare September 14, 2026 10:00
PiyushMishra318 added a commit to PiyushMishra318/apisix that referenced this pull request Sep 14, 2026
…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 0928862 to dac4ae2 Compare September 14, 2026 10:24
PiyushMishra318 added a commit to PiyushMishra318/apisix that referenced this pull request Sep 14, 2026
…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from dac4ae2 to 2e870ba Compare September 14, 2026 10:29
@PiyushMishra318
PiyushMishra318 marked this pull request as ready for review September 14, 2026 10:30
@janiussyafiq

Copy link
Copy Markdown
Contributor

Some initial comments:

  1. Fix CI and lint
  2. Add new test cases that cover this new feature/plugin

PiyushMishra318 added a commit to PiyushMishra318/apisix that referenced this pull request Sep 15, 2026
…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 2e870ba to 16f5018 Compare September 15, 2026 08:37
Comment thread t/plugin/openid-connect-consumer-selector.t Outdated
@janiussyafiq

Copy link
Copy Markdown
Contributor

Some issues remain:

  1. CI still failed. You can open up a PR against your fork to ensure CI pass.
  2. Please also add Chinese documentation, you can ask your agent to do that the translation from English to Chinese, if there are any issues, maintainers will point it out.
  3. Better not to force-push for easier review, can simply add a commit on top.

Thanks!

@PiyushMishra318

Copy link
Copy Markdown
Author

Some issues remain:

  1. CI still failed. You can open up a PR against your fork to ensure CI pass.
  2. Please also add Chinese documentation, you can ask your agent to do that the translation from English to Chinese, if there are any issues, maintainers will point it out.
  3. Better not to force-push for easier review, can simply add a commit on top.

Thanks!

Gotcha! Will do the same on my own fork first.

…ntime-variable templates

Continues apache#13038 with the direction requested there: keep
openid-connect generic and push request-identification/routing logic
into a separate, higher-priority plugin instead of baking IdP-specific
logic into openid-connect itself.

openid-connect: `discovery`, `client_id`, and `client_secret` now
resolve `${var}` / `${var ?? default}` templates from the request
context (via core.utils.resolve_var, the same mechanism limit-count
already uses), unconditionally, in rewrite(). An empty/nil resolution
fails closed with a 500 naming the field that could not be resolved,
instead of forwarding an empty value to the identity provider.

New companion plugin openid-connect-consumer-selector (priority 2895,
ahead of openid-connect's 2599) picks a config from a list of
candidates and exposes it via ctx.var.oidc_discovery / oidc_client_id
/ oidc_client_secret for openid-connect's templates to consume. Two
match modes, via `match_source`:

- "var" (default): match configs[].key against an arbitrary request
  variable named by `match_var`.
- "token_iss": read the Authorization: Bearer <token> header, decode
  the JWT payload without verifying its signature (same non-verifying
  use resty.jwt already gets in openid-connect.lua's session_scopes),
  and match configs[].key against the token's iss claim. Real
  signature verification still happens downstream in openid-connect
  against whichever config gets selected.

No IdP-specific logic is hard-coded in either plugin, so a single
route can serve multiple IdP realms/domains without baking any
provider knowledge into openid-connect.

client_secret is encrypted at rest for both the single config
(openid-connect, existing behavior) and each entry of the new
plugin's configs array (via encrypt_fields = {"configs.client_secret"}).

redirect_uri is intentionally not templated - out of scope for this
change.
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 5cca6be to 13edc82 Compare September 16, 2026 15:29
@PiyushMishra318
PiyushMishra318 marked this pull request as draft September 16, 2026 16:30
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch 4 times, most recently from 88fe184 to 63b5725 Compare September 17, 2026 04:54
- Add Chinese translation for the new selector plugin's docs
- Fix e2e login test flakiness (redirect_uri route mismatch, and a
  scoped-prefix route that didn't survive a full session-login flow;
  switched it to the catch-all route pattern proven elsewhere in the
  suite)
- Drop the unused var-match selector mode, keeping only token_iss
  based selection, matching the actual issuer-routing use case
- Rename openid-connect-consumer-selector -> openid-connect-idp-selector:
  the plugin never touches apisix.consumer or Consumer Groups, it only
  selects raw IdP config (discovery/client_id/client_secret) values, so
  "consumer selector" misnamed what it actually does
@PiyushMishra318
PiyushMishra318 force-pushed the feat/openid-connect-consumer-selector branch from 63b5725 to 137217b Compare September 17, 2026 04:57
@PiyushMishra318
PiyushMishra318 marked this pull request as ready for review September 17, 2026 09:14
@PiyushMishra318

PiyushMishra318 commented Sep 17, 2026 •

Copy link
Copy Markdown
Author

Hi @janiussyafiq,

  1. CI passed on my local fork you can check here Fork CI.
  2. Chinese Doc updated.
  3. Had to rebase once to get the latest main changed you might see one more force push due to that.

Let me know if there any logical or implementation specific changes that you want.

@shreemaan-abhishek shreemaan-abhishek Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need this (and the plugin declaration) anymore, do we?

Removal should include:

  • apisix/plugins/openid-connect-idp-selector.lua
  • Its plugin registration in apisix/cli/config.lua
  • Its entry in conf/config.yaml.example
  • English and Chinese selector documentation
  • Documentation navigation entries
  • t/plugin/openid-connect-idp-selector.t
  • Admin plugin-list expectations
  • References from openid-connect.md that present it as the built-in selector


### Selecting an IdP configuration per request

`client_id`, `client_secret`, and `discovery` also support `${var}` / `${var ?? default}` runtime-variable templates, resolved from the request context on every request (the same templating [`limit-count`](./limit-count.md) uses for `count`/`time_window`). This lets a higher-priority custom Plugin inspect the request, set `ctx.var.*`, and select the right IdP configuration for it, while `openid-connect` itself stays generic — it has no built-in notion of tenants, realms, or providers. See [`openid-connect-idp-selector`](./openid-connect-idp-selector.md) for a Plugin that does this based on the request's bearer token issuer.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI: to capitalise on the new feature of templated client_id, client_secret, and discovery, clients can directly send request headers and use http_<header_name> in the template.

having a custom plugin that would inspect the request and fill a ctx.var.** is not compulsory.

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

pls correct the PR scope
- Describe the feature as generic per-request templating of client_id, client_secret, and discovery.
- Do not claim it directly selects Consumers or Consumer Groups.

@shreemaan-abhishek

shreemaan-abhishek commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

The tests should include at least one successful OIDC authentication using runtime-resolved values. Abd a following request with different variable values, confirming the shared plugin configuration was not mutated or
leaked between requests.

@shreemaan-abhishek

Copy link
Copy Markdown
Contributor

also, the unconditional variable resolution of the fields client_id, client_secret, and discovery could be a breaking change because client_id/client_secret may contain $.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request plugin size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: As a user, I want to select a Consumer (and its Consumer Group) from a JWT claim in openid-connect on a single route.

5 participants