Skip to content

feat: add "valid_issuers" field in openidc plugin#12002

Merged
Revolyssup merged 26 commits into
apache:masterfrom
Revolyssup:revolyssup/oidc
Mar 11, 2025
Merged

feat: add "valid_issuers" field in openidc plugin#12002
Revolyssup merged 26 commits into
apache:masterfrom
Revolyssup:revolyssup/oidc

Conversation

@Revolyssup

@Revolyssup Revolyssup commented Feb 27, 2025

Copy link
Copy Markdown
Contributor

Description

Adds a field valid_issuers when jwks is used to verify the issuer of jwt.
Whitelist the vetted issuers of the jwt.
When not passed by the user, the issuer returned by discovery endpoint will be used.
In case both are missing, the issuer will not be validated.

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 the size:L This PR changes 100-499 lines, ignoring generated files. label Feb 27, 2025
@Revolyssup
Revolyssup marked this pull request as draft February 27, 2025 08:20
@dosubot dosubot Bot added the enhancement New feature or request label Feb 27, 2025
Comment thread apisix/plugins/openid-connect.lua Outdated
@Revolyssup
Revolyssup marked this pull request as ready for review February 27, 2025 10:03

@nic-chen nic-chen 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.

please add test cases for verifying the issuer returned by discovery endpoint

nic-6443
nic-6443 previously approved these changes Feb 28, 2025

@nic-6443 nic-6443 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.

LGTM

@Revolyssup

Copy link
Copy Markdown
Contributor Author

please add test cases for verifying the issuer returned by discovery endpoint

Added. Please review again.

Comment thread t/plugin/openid-connect7.t Outdated
@Revolyssup

Copy link
Copy Markdown
Contributor Author

@nic-chen Okay I think the last test can be done without the mock server. I have removed that and now the token verification happens as well. You can review

nic-chen
nic-chen previously approved these changes Feb 28, 2025
nic-6443
nic-6443 previously approved these changes Feb 28, 2025
Comment thread apisix/plugins/openid-connect.lua Outdated
opts.valid_issuers = valid_issuers
end
end
if conf.public_key or conf.use_jwks then

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.

If public_key is specified, it will not verify issuers. Are you sure this is expected?

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.

you are right, we should verify issuer when use public_key too.

Comment thread apisix/plugins/openid-connect.lua Outdated
Comment on lines +279 to +287
valid_issuers = {
description = [[Whitelist the vetted issuers of the jwt.
When not passed by the user, the issuer returned by discovery endpoint will be used.
In case both are missing, the issuer will not be validated.]],
type = "array",
items = {
type = "string"
}
},

@bzp2010 bzp2010 Feb 28, 2025

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.

This has already had another implementation that was approved and we have to decide which one is better.

#11987

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Your PR adds audience validation and my PR adds issuer validation.
Audience validation matches "aud" in the JWT with "client_id"
Issuer validation matches "issuer" in the JWT with discovery.issuer/or passed by user.

If the problem that I am trying to solve is this "I want to reject a request because customer passed a JWT with unrecognised issuer."

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

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.

@Revolyssup 🙌

I certainly know what I did, and know what you did, and I mentioned that we have to choose one of the better configuration schema designs, while accommodating design that are similar in use but so different is unacceptable.
Specifically, if they are tiled, then this and all subsequent validator configuration items need to be tiled, otherwise it's going to be structured like that PR #11987.

That's exactly why that PR hasn't merged yet, it's waiting for some feedback.

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.

@Revolyssup please refer to #11987 to update the configuration schema, thanks.

@Revolyssup
Revolyssup dismissed stale reviews from nic-6443 and nic-chen via 580a515 February 28, 2025 05:42
@dosubot dosubot Bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Mar 6, 2025
nic-6443
nic-6443 previously approved these changes Mar 7, 2025
nic-chen
nic-chen previously approved these changes Mar 7, 2025
@nvx

nvx commented Mar 7, 2025

Copy link
Copy Markdown

This change looks good and should resolve the issue I raised.

~Tiernan


-- If we get here, token was found in request.

if conf.public_key or conf.use_jwks then

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.

When using the Introspection API, don't we check the issuer again locally? If so, it seems a bit confusing.

I.e. when exactly does the configuration of “valid_issuers” take effect. If this is as expected, it should be documented.

Comment thread apisix/plugins/openid-connect.lua Outdated

local valid_issuers
if conf.claim_validator and conf.claim_validator.valid_issuers then
valid_issuers = conf.claim_validator.valid_issuers

@bzp2010 bzp2010 Mar 7, 2025

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 understand that this valid_issuers is one of the options in lua-resty-jwt, but this is not exactly for OpenID Connect (JWT subset), but for whole JWT.
As documented in the OpenID Connect Core 1.0 specification, it should be present and must be equal to an exact match of the issuer identifier (obtained from discovery), and it seems that this value should not be configurable by the user, or according to the OIDC specification, the user doesn't need to configure this value.

Ref:

An iss (issuer) Claim SHOULD be included in any JWT issued by a Claims Provider so that the Claims Provider's keys can be retrieved for signature validation of the JWT. The value of the Claim is the Claims Provider's Issuer Identifier URL.
https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims

It is mentioned here that the iss declaration must be present in any IDP-issued JWT, so this is not limited to ID Token, but also Access Token.

The Issuer Identifier for the OpenID Provider (which is typically obtained during Discovery) MUST exactly match the value of the iss (issuer) Claim.
https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation

Although this is the section used to describe the ID Token rules, it mentions the exact rules about iss claim.

issuer: REQUIRED. [skiped] This also MUST be identical to the iss Claim value in ID Tokens issued from this Issuer.
https://openid.net/specs/openid-connect-discovery-1_0-final.html#ProviderMetadata

The issuer must be included in the discovery response.


So if the use case here is oriented towards openid connect, and IDP servers that do conform to openid connect, the value should not even be allowed to be user-defined, but always obtained from discovery.
But I understand that we may have to deal with some corner cases, then the configuration design should explicitly reflect that this is an override of the default behavior (which according to the spec iss validation should be mandatory).

So I would propose a design similar to the audience one:

{
  "claim_validator": {
    // !!! If you feel that it is sufficient for us to obey the OIDC specification (i.e. not need allow the user to customize the issuer list), then this field can be simply specified as boolean, which defaults to true.
    "issuer": true,
    // !!! Otherwise a nested structure can be provided that has two settings, whether to enforce issuer validation or not, and another to store a list of custom issuers.
    "issuer": {
      "enforce": true // I'm not sure what it should be called, but this should indicate if we MUST perform the iss check.
      "valid_issuers": ["xxx"]
    }
  }
}

WDYT?

  • Must users still be allowed to customize the issuer list?
  • How should the configuration look?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think the users should be able to customise the list, that was the original requirement for this from the customer.
From usage perspective, I think the original configuration is simple

{
  "claim_validator": {
      "valid_issuers": []
   }
}

If valid_issuers is not explicitly set then it is understood that there wont be issuer verification.
The use case this tries to solve is to make sure that even if discovery endpoint returns a different issuers in response, user can override that by manually passing the valid_issuer.

@bzp2010 bzp2010 Mar 10, 2025

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.

The claim_validator should only store claims without any prefixes, just like audience. The valid_issuers clearly violates this convention.

I've made proposals that would satisfy either the option to support setting arbitrary issuers or the strict OIDC specification of not supporting arbitrary issuer setting.

  • If OIDC is strictly followed, use issuer: true
  • If customization is allowed, use nested structured configuration.

If no agreement can be reached on this, then other reviewers will need to comment on this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Okay second option looks good to me.

Comment thread apisix/plugins/openid-connect.lua Outdated
@bzp2010
bzp2010 self-requested a review March 10, 2025 11:05
@Revolyssup
Revolyssup requested a review from membphis March 11, 2025 05:01
@Revolyssup
Revolyssup merged commit 2938f41 into apache:master Mar 11, 2025
laz-xyr pushed a commit to laz-xyr/apisix that referenced this pull request Jun 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants