Skip to content

Commit 96fc950

Browse files
authored
Release/3.11.3 (#640)
* Add configurable issuer setting for JWT validation (#639) Fixes issuer mismatch errors when the IDP's issuer differs from the login endpoint base URL. The issuer can be configured via the Issuer setting, OIDC_ISSUER constant, or auto-populated from discovery documents. Defaults to deriving from endpoint_login for backward compatibility. * 3.11.3 * Log issuer mismatch details for admins to fix the problem * Release 3.11.3 version bump and tasks
1 parent 1810fff commit 96fc950

13 files changed

Lines changed: 277 additions & 122 deletions

CHANGELOG.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,61 @@
11
# OpenId Connect Generic Changelog
22

3+
**3.11.3**
4+
5+
* Feature/improvement: Added configurable issuer setting for JWT validation.
6+
7+
**3.11.2**
8+
9+
* Improvement: Support identity providers that omit algorithm parameter in JWKS (Microsoft Entra ID).
10+
11+
**3.11.1**
12+
13+
* Fix bug created in 3.11.0 release when comparing issuer to derived expected value.
14+
15+
**3.11.0**
16+
17+
**SECURITY RELEASE**
18+
19+
* Security: Added JWT signature verification using JWKS to prevent token forgery
20+
* Security: Enhanced token claim validation (exp, aud, iss, iat, nonce)
21+
* Security: Replaced weak state generation with cryptographically secure random_bytes()
22+
* Security: Fixed open redirect vulnerability in authentication flow
23+
* Security: Restricted SSL verification bypass to local development environments only
24+
* Security: Added nonce protection to debug mode to prevent information disclosure
25+
* Security: Added SSRF protection by default through use of wp_safe_remote_* functions
26+
* Feature: Added JWKS endpoint configuration setting
27+
* Feature: Added OpenID Connect discovery document support
28+
* Feature: Added customizable login button text setting
29+
* Improvement: Migrated to Composer-managed dependencies
30+
* Fix: Corrected issuer validation to properly extract base URL from endpoints
31+
* Fix: Identity token timestamp tracking
32+
33+
**3.10.4**
34+
35+
* Fix issue with finding users on multisite after switch to user options in place of user meta.
36+
* Improvement: Retry logins for some IDP errors to bypass issue with Safari ITP. Also improves display of error messages that come from the IDP.
37+
38+
**3.10.3**
39+
40+
* Fix issue with log corruption causing fatal error.
41+
* Fix: Fallback to a POST request for userinfo when GET fails.
42+
* Fix: Improves multisite compatibility by switching to *_user_options() functions.
43+
* Fix: Fix for WordPress user session length being very short when refresh tokens are enabled.
44+
45+
**3.10.2**
46+
47+
* Fix: @socialmedialabs - Regression affecting SSO Auto Login with url handling improvement changes.
48+
49+
**3.10.1**
50+
51+
* Chore: @daggerhart - Readme updates and clarifications.
52+
* Chore: @daggerhart - Release workflow updates.
53+
* Improved error handling for malformed urls.
54+
* Fix: @JUVOJustin - Change request for userinfo to GET.
55+
* Feature: @JUVOJustin - New filter for settings values `openid-connect-generic-settings`.
56+
* Feature: @JUVOJustin - New filter for state values `openid-connect-generic-new-state-value`.
57+
58+
359
**3.10.0**
460

561
- Chore: @timnolte - Dependency updates.

README.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
**Tags:** security, login, oauth2, openidconnect, apps, authentication, autologin, sso
44
**Requires at least:** 5.0
55
**Tested up to:** 6.9.0
6-
**Stable tag:** 3.11.2
6+
**Stable tag:** 3.11.3
77
**Requires PHP:** 7.4
88
**License:** GPLv2 or later
99
**License URI:** http://www.gnu.org/licenses/gpl-2.0.html
@@ -49,12 +49,16 @@ On the settings page for this plugin (Dashboard > Settings > OpenID Connect Gene
4949

5050
## Upgrade Notice ##
5151

52-
### 3.11.2 ###
52+
### 3.11.3 ###
5353

54-
CRITICAL SECURITY UPDATE: 3.11.x Fixes authentication vulnerabilities including JWT signature bypass and SSRF protection. Update immediately and configure JWKS endpoint in settings.
54+
SECURITY UPDATE: 3.11.x branch - Fixes authentication vulnerabilities including JWT signature bypass and SSRF protection. Update immediately and configure JWKS endpoint in settings.
5555

5656
## Changelog ##
5757

58+
### 3.11.3 ###
59+
60+
* Feature/improvement: Added configurable issuer setting for JWT validation.
61+
5862
### 3.11.2 ###
5963

6064
* Improvement: Support identity providers that omit algorithm parameter in JWKS (Microsoft Entra ID).

includes/openid-connect-generic-client-wrapper.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -914,11 +914,16 @@ private function get_claim( $claimname, $userinfo, &$claimvalue ) {
914914

915915
// Check if JWKS endpoint is configured for JWT signature verification.
916916
if ( ! empty( $this->settings->endpoint_jwks ) ) {
917+
// Use configured issuer if provided, otherwise derive from endpoint_login.
918+
$issuer = ! empty( $this->settings->issuer ) ?
919+
$this->settings->issuer :
920+
( ! empty( $this->settings->endpoint_login ) ? $this->client->get_issuer_from_endpoint( $this->settings->endpoint_login ) : '' );
921+
917922
// Use JWT validator for secure signature verification.
918923
$jwt_validator = new OpenID_Connect_Generic_JWT_Validator(
919924
$this->settings->endpoint_jwks,
920925
$this->settings->client_id,
921-
$this->client->get_issuer_from_endpoint( $this->settings->endpoint_login ),
926+
$issuer,
922927
$this->settings->jwks_cache_ttl,
923928
$this->settings->allow_internal_idp,
924929
$this->logger

includes/openid-connect-generic-client.php

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ class OpenID_Connect_Generic_Client {
100100
*/
101101
private $endpoint_jwks;
102102

103+
/**
104+
* The issuer URL for JWT validation.
105+
*
106+
* @see OpenID_Connect_Generic_Option_Settings::issuer
107+
*
108+
* @var string
109+
*/
110+
private $issuer;
111+
103112
/**
104113
* The JWKS cache TTL in seconds.
105114
*
@@ -146,12 +155,13 @@ class OpenID_Connect_Generic_Client {
146155
* @param string $redirect_uri @see OpenID_Connect_Generic_Option_Settings::redirect_uri for description.
147156
* @param string $acr_values @see OpenID_Connect_Generic_Option_Settings::acr_values for description.
148157
* @param string $endpoint_jwks @see OpenID_Connect_Generic_Option_Settings::endpoint_jwks for description.
158+
* @param string $issuer @see OpenID_Connect_Generic_Option_Settings::issuer for description.
149159
* @param int $jwks_cache_ttl @see OpenID_Connect_Generic_Option_Settings::jwks_cache_ttl for description.
150160
* @param int $state_time_limit @see OpenID_Connect_Generic_Option_Settings::state_time_limit for description.
151161
* @param bool $allow_internal_idp @see OpenID_Connect_Generic_Option_Settings::allow_internal_idp for description.
152162
* @param OpenID_Connect_Generic_Option_Logger $logger The plugin logging object instance.
153163
*/
154-
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $acr_values, $endpoint_jwks, $jwks_cache_ttl, $state_time_limit, $allow_internal_idp, $logger ) {
164+
public function __construct( $client_id, $client_secret, $scope, $endpoint_login, $endpoint_userinfo, $endpoint_token, $redirect_uri, $acr_values, $endpoint_jwks, $issuer, $jwks_cache_ttl, $state_time_limit, $allow_internal_idp, $logger ) {
155165
$this->client_id = $client_id;
156166
$this->client_secret = $client_secret;
157167
$this->scope = $scope;
@@ -161,6 +171,7 @@ public function __construct( $client_id, $client_secret, $scope, $endpoint_login
161171
$this->redirect_uri = $redirect_uri;
162172
$this->acr_values = $acr_values;
163173
$this->endpoint_jwks = $endpoint_jwks;
174+
$this->issuer = $issuer;
164175
$this->jwks_cache_ttl = $jwks_cache_ttl;
165176
$this->state_time_limit = $state_time_limit;
166177
$this->allow_internal_idp = $allow_internal_idp;
@@ -543,11 +554,16 @@ public function get_id_token_claim( $token_response ) {
543554

544555
// Check if JWKS endpoint is configured for JWT signature verification.
545556
if ( ! empty( $this->endpoint_jwks ) ) {
557+
// Use configured issuer if provided, otherwise derive from endpoint_login.
558+
$issuer = ! empty( $this->issuer )
559+
? $this->issuer
560+
: $this->get_issuer_from_endpoint( $this->endpoint_login );
561+
546562
// Use JWT validator for secure signature verification.
547563
$jwt_validator = new OpenID_Connect_Generic_JWT_Validator(
548564
$this->endpoint_jwks,
549565
$this->client_id,
550-
$this->get_issuer_from_endpoint( $this->endpoint_login ),
566+
$issuer,
551567
$this->jwks_cache_ttl,
552568
$this->allow_internal_idp,
553569
$this->logger
@@ -671,16 +687,25 @@ public function validate_id_token_claim( $id_token_claim ) {
671687
return new WP_Error( 'invalid-aud', __( 'Token audience does not match client.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
672688
}
673689

674-
// Validate issuer claim if endpoint_login is configured.
675-
if ( ! empty( $this->endpoint_login ) ) {
690+
// Validate issuer claim if configured or endpoint_login is available.
691+
$expected_issuer = ! empty( $this->issuer ) ?
692+
$this->issuer :
693+
( ! empty( $this->endpoint_login ) ? $this->get_issuer_from_endpoint( $this->endpoint_login ) : '' );
694+
695+
if ( ! empty( $expected_issuer ) ) {
676696
if ( ! isset( $id_token_claim['iss'] ) ) {
677697
return new WP_Error( 'missing-iss', __( 'Token missing issuer claim.', 'daggerhart-openid-connect-generic' ), $id_token_claim );
678698
}
679699

680-
// Extract expected issuer from endpoint_login (base URL).
681-
$expected_issuer = $this->get_issuer_from_endpoint( $this->endpoint_login );
682-
683700
if ( rtrim( $id_token_claim['iss'], '/' ) !== rtrim( $expected_issuer, '/' ) ) {
701+
$this->logger->log(
702+
sprintf(
703+
'Issuer mismatch - Expected: "%s", Received: "%s". Configure the correct issuer in Settings > OpenID Connect Client > Issuer field, or via the OIDC_ISSUER constant.',
704+
$expected_issuer,
705+
$id_token_claim['iss']
706+
),
707+
'issuer-mismatch'
708+
);
684709
return new WP_Error(
685710
'invalid-iss',
686711
sprintf(

includes/openid-connect-generic-jwt-validator.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,14 @@ private function validate_jwt_claims( $decoded_jwt ) {
228228
}
229229

230230
if ( rtrim( $decoded_jwt->iss, '/' ) !== rtrim( $this->issuer, '/' ) ) {
231+
$this->logger->log(
232+
sprintf(
233+
'Issuer mismatch - Expected: "%s", Received: "%s". Configure the correct issuer in Settings > OpenID Connect Client > Issuer field, or via the OIDC_ISSUER constant.',
234+
$this->issuer,
235+
$decoded_jwt->iss
236+
),
237+
'issuer-mismatch'
238+
);
231239
return new WP_Error(
232240
'invalid-iss',
233241
__( 'Token issuer does not match expected issuer.', 'daggerhart-openid-connect-generic' )

includes/openid-connect-generic-option-settings.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
* @property string $endpoint_token The IDP token validation endpoint URL.
3636
* @property string $endpoint_end_session The IDP logout endpoint URL.
3737
* @property string $endpoint_jwks The IDP JWKS endpoint URL for JWT signature verification.
38+
* @property string $issuer The IDP issuer URL for JWT validation (optional - derived from endpoint_login if not set).
3839
* @property int $jwks_cache_ttl The JWKS cache TTL in seconds.
3940
* @property string $acr_values The Authentication contract as defined on the IDP.
4041
*
@@ -98,6 +99,7 @@ class OpenID_Connect_Generic_Option_Settings {
9899
'endpoint_token' => 'OIDC_ENDPOINT_TOKEN_URL',
99100
'endpoint_userinfo' => 'OIDC_ENDPOINT_USERINFO_URL',
100101
'endpoint_jwks' => 'OIDC_ENDPOINT_JWKS_URL',
102+
'issuer' => 'OIDC_ISSUER',
101103
'login_type' => 'OIDC_LOGIN_TYPE',
102104
'scope' => 'OIDC_CLIENT_SCOPE',
103105
'create_if_does_not_exist' => 'OIDC_CREATE_IF_DOES_NOT_EXIST',

includes/openid-connect-generic-settings-page.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,14 @@ private function get_settings_fields() {
308308
'disabled' => defined( 'OIDC_ENDPOINT_JWKS_URL' ),
309309
'section' => 'client_settings',
310310
),
311+
'issuer' => array(
312+
'title' => __( 'Issuer', 'daggerhart-openid-connect-generic' ),
313+
'description' => __( 'Identity provider issuer URL for JWT validation. If not set, the issuer will be automatically derived from the Login Endpoint URL. Only configure this if your IDP uses a different issuer than the base URL of the login endpoint.', 'daggerhart-openid-connect-generic' ),
314+
'example' => 'https://example.com',
315+
'type' => 'text',
316+
'disabled' => defined( 'OIDC_ISSUER' ),
317+
'section' => 'client_settings',
318+
),
311319
'jwks_cache_ttl' => array(
312320
'title' => __( 'JWKS Cache TTL (seconds)', 'daggerhart-openid-connect-generic' ),
313321
'description' => __( 'Time in seconds to cache JWKS keys. Default: 3600 (1 hour)', 'daggerhart-openid-connect-generic' ),
@@ -776,6 +784,7 @@ private function populate_settings_from_discovery( $discovery ) {
776784
'token_endpoint' => 'endpoint_token',
777785
'userinfo_endpoint' => 'endpoint_userinfo',
778786
'jwks_uri' => 'endpoint_jwks',
787+
'issuer' => 'issuer',
779788
'end_session_endpoint' => 'endpoint_end_session',
780789
);
781790

0 commit comments

Comments
 (0)