• Resolved remiCs

    (@emersong20)


    The get_issuer_from_endpoint() function incorrectly extracts the issuer URL by only using scheme://host, ignoring the subdirectory path. This causes JWT validation to fail when the Identity Provider’s issuer includes a subdirectory (e.g., https://example.com/central).


    ## Environment

    – **Plugin Version:** 3.11.2
    – **WordPress Version:** 6.9.1
    – **PHP Version:** 8.3
    – **Identity Provider:** Custom OAuth server (WHMCS-based)

    ## Steps to Reproduce

    1. Configure the plugin with an Identity Provider that has endpoints in a subdirectory:
    – Authorization Endpoint: https://example.com/central/oauth/authorize.php
    – Token Endpoint: https://example.com/central/oauth/token.php
    – JWKS Endpoint: https://example.com/central/oauth/certs.php

    2. The Identity Provider returns JWT tokens with iss claim:
    json<br> {<br> "iss": "https://example.com/central",<br> ...<br> }<br>

    3. Attempt to authenticate via OpenID Connect

    4. **Result:** Authentication fails with error:
    <br> ERROR (invalid-iss): Token issuer does not match expected issuer.<br>

    ## Root Cause Analysis

    The get_issuer_from_endpoint() function in includes/openid-connect-generic-client.php only extracts scheme://host from the endpoint URL:

    php<br>public function get_issuer_from_endpoint( $endpoint_url ) {<br> $parsed = wp_parse_url( $endpoint_url );<br> <br> // ...<br> <br> $issuer = $parsed['scheme'] . '://' . $parsed['host']; // ❌ BUG HERE<br> <br> // ...<br> <br> return $issuer;<br>}<br>

    **Debug logs show the mismatch:**
    <br>Endpoint URL received: https://example.com/central/oauth/authorize.php<br>Issuer calculated: https://example.com/central ❌ (missing /central)<br>Issuer in Token: https://example.com/central ✅<br>

    The function returns https://example.com instead of https://example.com/central, causing the validation to fail.

    ## Proposed Fix

    Modify the get_issuer_from_endpoint() function to extract the base path from the endpoint URL:

    php<br>public function get_issuer_from_endpoint( $endpoint_url ) {<br> $parsed = wp_parse_url( $endpoint_url );<br><br> if ( ! $parsed || ! isset( $parsed['scheme'] ) || ! isset( $parsed['host'] ) ) {<br> return $endpoint_url;<br> }<br><br> $issuer = $parsed['scheme'] . '://' . $parsed['host'];<br><br> // Add port if non-standard.<br> if ( isset( $parsed['port'] ) ) {<br> $default_ports = array(<br> 'http' => 80,<br> 'https' => 443,<br> );<br> if ( ! isset( $default_ports[ $parsed['scheme'] ] ) || $parsed['port'] != $default_ports[ $parsed['scheme'] ] ) {<br> $issuer .= ':' . $parsed['port'];<br> }<br> }<br><br> // === FIX: Include base path in issuer ===<br> if ( ! empty( $parsed['path'] ) ) {<br> // Remove the endpoint filename (e.g., authorize.php)<br> $path = dirname( $parsed['path'] );<br> <br> // If there's a parent directory beyond root (e.g., /central/oauth -> /central)<br> if ( $path !== '/' && $path !== '.' ) {<br> // Go up one level to get the base path<br> $base_path = dirname( $path );<br> if ( $base_path !== '/' && $base_path !== '.' ) {<br> $issuer .= $base_path;<br> }<br> }<br> }<br><br> return $issuer;<br>}<br>

    ## Alternative Approaches

    ### Option A: Use OpenID Discovery (Preferred)

    If the Identity Provider provides a /.well-known/openid-configuration endpoint, the issuer should be fetched from there rather than extracted from the URL.

    ### Option B: Add Manual Issuer Configuration

    Allow users to manually specify the expected issuer in the plugin settings when automatic detection fails.

    ### Option C: Use rtrim() Comparison (Partial Fix – Already Merged)

    The recent commit 7c9d659 added rtrim() to handle trailing slash differences, but this doesn’t solve the subdirectory path issue:
    php<br>if ( rtrim( $id_token_claim['iss'], '/' ) !== rtrim( $expected_issuer, '/' ) ) {<br>

    ## Impact

    This bug affects all users whose Identity Provider:
    – Uses a subdirectory path (e.g., /central, /idp, /auth)
    – Returns an issuer claim that includes the subdirectory path

    Common affected scenarios:
    – Self-hosted OAuth servers (WHMCS, Keycloak with context path, etc.)
    – Identity Providers behind reverse proxies with path-based routing
    – Multi-tenant OAuth servers

    ## Related

    – Commit 7c9d65929fabb4fb71590f702d85ff208519443d – Fixed trailing slash comparison
    – Issue #629 – Related issuer validation improvements

    Thank you for maintaining this excellent plugin! Please let me know if you need any additional information or testing assistance.

Viewing 3 replies - 1 through 3 (of 3 total)
Viewing 3 replies - 1 through 3 (of 3 total)

The topic ‘Bug: “invalid-iss” error’ is closed to new replies.