function rp_decrypt_verify_id_token($id_token)
{
    $response = array();
    $jwt_parts = jwt_to_array($id_token);
    if (isset($jwt_parts[0]['enc'])) {
        // encrypted
        echo "Encrypted ID Token - {$jwt_parts[0]['enc']} {$jwt_parts[0]['alg']} {$jwt_parts[0]['int']}\n";
        $response['jwe'] = $jwt_parts;
        $signed_jwt = jwt_decrypt($id_token, RP_PKEY, true);
        if (!$signed_jwt) {
            echo "Unable to decrypt ID Token response";
            return false;
        }
    } else {
        // signed
        $signed_jwt = $id_token;
        // echo "Signed ID Token {$jwt_parts[0]['alg']}\n";
    }
    if ($signed_jwt) {
        list($header, $payload, $sig) = jwt_to_array($signed_jwt);
        // echo "Signed ID Token {$header['alg']}\n";
        $response['jws'] = array($header, $payload, $sig);
        if ($header['alg'] == 'none') {
            $verified = true;
        } else {
            $verified = jwt_verify($signed_jwt);
        }
        // echo "Signature Verification = $verified";
        if ($verified) {
            if (isset($payload['address']) && is_array($payload['address'])) {
                if (isset($payload['address']['formatted'])) {
                    $payload['address'] = $payload['address']['formatted'];
                } else {
                    $payload['address'] = "{$payload['address']['street_address']}\n{$payload['address']['locality']}, {$payload['address']['region']} {$payload['address']['postal_code']}\n{$payload['address']['country']}";
                }
            }
            if (isset($payload['aud']) && is_array($payload['aud'])) {
                $payload['aud'] = implode(', ', $payload['aud']);
            }
            $g_id_response = $payload;
            // echo "ID Token Signature Verified\n";
            $_SESSION['login'] = $payload['sub'];
        } else {
            echo "ID Token Signature Verification Failed\n";
        }
    }
    return $response;
}
function is_client_authenticated()
{
    try {
        $auth_type = '';
        if (isset($_REQUEST['client_assertion_type'])) {
            $auth_type = $_REQUEST['client_assertion_type'];
            log_debug("client_assertion_type auth %s", $auth_type);
            if ($auth_type != 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer') {
                throw new OidcException('unauthorized_client', 'Unknown client_assertion_type');
            }
            $jwt_assertion = $_REQUEST['client_assertion'];
            if (!isset($jwt_assertion)) {
                throw new OidcException('unauthorized_client', 'client_assertion not available');
            }
            list($jwt_header, $jwt_payload, $jwt_sig) = jwt_to_array($jwt_assertion);
            if ($jwt_payload['iss'] != $jwt_payload['sub']) {
                throw new OidcException('invalid request', 'JWT iss and prn mismatch');
            }
            $client_id = $jwt_payload['iss'];
            log_debug("header = %s\npayload = %s\n", print_r($jwt_header, true), print_r($jwt_payload, true));
            log_debug("assertion = %s", $jwt_assertion);
            $alg_prefix = substr($jwt_header['alg'], 0, 2);
            if ($alg_prefix == "HS") {
                $auth_type = 'client_secret_jwt';
            } elseif ($alg_prefix == "RS") {
                $auth_type = 'private_key_jwt';
            }
            log_debug("auth_type = %s", $auth_type);
        } elseif (isset($_SERVER['PHP_AUTH_USER'])) {
            $client_id = $_SERVER['PHP_AUTH_USER'];
            if (isset($_SERVER['PHP_AUTH_PW'])) {
                $client_secret = $_SERVER['PHP_AUTH_PW'];
            }
            $auth_type = 'client_secret_basic';
        } elseif (isset($_REQUEST['client_id'])) {
            $client_id = $_REQUEST['client_id'];
            if (isset($_REQUEST['client_secret'])) {
                $client_secret = $_REQUEST['client_secret'];
            }
            $auth_type = 'client_secret_post';
        } else {
            throw new OidcException('invalid_request', 'Unknown authentication type');
        }
        if (!$client_id || !($client_secret || $jwt_assertion)) {
            throw new OidcException('invalid_client', 'no client or secret');
        }
        // perform client_id and client_secret check
        $db_client = db_get_client($client_id);
        if ($db_client) {
            log_debug("%s\n%s", $db_client['client_id'], $db_client['token_endpoint_auth_method']);
            $db_client = $db_client->toArray();
            $token_endpoint_auth_method = $db_client['token_endpoint_auth_method'];
            if (!$token_endpoint_auth_method) {
                $token_endpoint_auth_method = 'client_secret_basic';
            }
        } else {
            throw new OidcException('unauthorized_client', 'client_id not found');
        }
        if ($token_endpoint_auth_method != $auth_type) {
            throw new OidcException('unauthorized_client', "mismatched token endpoint auth type {$auth_type} != {$db_client['token_endpoint_auth_method']}");
        }
        switch ($token_endpoint_auth_method) {
            case 'client_secret_basic':
            case 'client_secret_post':
                $client_authenticated = db_check_client_credential($client_id, $client_secret);
                log_info("authenticating client_id %s with client_secret %s\nResult : %d", $client_id, $client_secret, $client_authenticated);
                break;
            case 'client_secret_jwt':
                $sig_verified = jwt_verify($jwt_assertion, $db_client['client_secret']);
                if ($db_client['token_endpoint_auth_signing_alg']) {
                    $alg_verified = $db_client['token_endpoint_auth_signing_alg'] == $jwt_header['alg'];
                } else {
                    $alg_verified = true;
                }
                if (substr($_SERVER['PATH_INFO'], 0, 2) == '/1') {
                    $audience = OP_ENDPOINT . '/1/token';
                } else {
                    $audience = OP_ENDPOINT . '/token';
                }
                $aud_verified = (is_array($jwt_payload['aud']) ? $jwt_payload['aud'][0] : $jwt_payload['aud']) == $audience;
                $now = time();
                $time_verified = abs($now - $jwt_payload['iat'] <= 180) && abs($now - $jwt_payload['exp'] < 180);
                if (!$sig_verified) {
                    log_info("Sig not verified");
                }
                if (!$aud_verified) {
                    log_info("Aud not verified %s != %s", $jwt_payload['aud'], $audience);
                }
                if (!$time_verified) {
                    log_info('Time not verified');
                }
                if (!$alg_verified) {
                    log_info("Signing Alg does not match %s != %s", $jwt_header['alg'], $db_client['token_endpoint_auth_signing_alg']);
                }
                $client_authenticated = $sig_verified && $aud_verified && $time_verified && $alg_verified;
                log_info(" client_secret_jwt Result : %d %d %d %d %d", $client_authenticated, $sig_verified, $aud_verified, $time_verified, $alg_verified);
                break;
            case 'private_key_jwt':
                $pubkeys = array();
                if ($db_client['jwks_uri']) {
                    $pubkeys['jku'] = $db_client['jwks_uri'];
                }
                if ($db_client['jwks']) {
                    $pubkeys['jwk'] = $db_client['jwks'];
                }
                $sig_verified = jwt_verify($jwt_assertion, $pubkeys);
                if ($db_client['token_endpoint_auth_signing_alg']) {
                    $alg_verified = $db_client['token_endpoint_auth_signing_alg'] == $jwt_header['alg'];
                } else {
                    $alg_verified = true;
                }
                if (substr($_SERVER['PATH_INFO'], 0, 2) == '/1') {
                    $audience = OP_ENDPOINT . '/1/token';
                } else {
                    $audience = OP_ENDPOINT . '/token';
                }
                $aud_verified = (is_array($jwt_payload['aud']) ? $jwt_payload['aud'][0] : $jwt_payload['aud']) == $audience;
                $now = time();
                $time_verified = abs($now - $jwt_payload['iat'] <= 180) && abs($now - $jwt_payload['exp'] < 180);
                if (!$sig_verified) {
                    log_info("Sig not verified");
                }
                if (!$aud_verified) {
                    log_info('Aud not verified');
                }
                if (!$time_verified) {
                    log_info('Time not verified');
                }
                if (!$alg_verified) {
                    log_info("Signing Alg does not match %s != %s", $jwt_header['alg'], $db_client['token_endpoint_auth_signing_alg']);
                }
                $client_authenticated = $sig_verified && $aud_verified && $time_verified && $alg_verified;
                log_info("private_key_jwt Result : %d %d %d %d %d", $client_authenticated, $sig_verified, $aud_verified, $time_verified, $alg_verified);
                break;
            default:
                throw new OidcException('invalid_request', 'Unknown authentication type');
        }
        return $client_authenticated;
    } catch (OidcException $e) {
        send_error(NULL, $e->error_code, $e->desc);
    } catch (Exception $e) {
        send_error(NULL, '', $e->getMessage() . ' ' . $e->getTraceAsString());
    }
    return false;
}
function rp_decrypt_verify_id_token($id_token)
{
    global $g_id_response, $g_info, $g_error;
    $response = array();
    $jwt_parts = jwt_to_array($id_token);
    if (isset($jwt_parts[0]['enc'])) {
        // encrypted
        $g_info .= "Encrypted ID Token - {$jwt_parts[0]['enc']} {$jwt_parts[0]['alg']} {$jwt_parts[0]['int']}\n";
        $response['jwe'] = $jwt_parts;
        $signed_jwt = jwt_decrypt($id_token, RP_ENC_PKEY, true, RP_ENC_PKEY_PASSPHRASE);
        if (!$signed_jwt) {
            $g_error .= "Unable to decrypt ID Token response";
            log_error('%s', $g_error);
            return;
        }
    } else {
        // signed
        $signed_jwt = $id_token;
        $g_info .= "Signed ID Token {$jwt_parts[0]['alg']}\n";
    }
    if ($signed_jwt) {
        list($header, $payload, $sig) = jwt_to_array($signed_jwt);
        $g_info .= "Signed ID Token {$header['alg']}\n";
        $response['jws'] = array($header, $payload, $sig);
        if (substr($header['alg'], 0, 2) == 'HS') {
            $verified = jwt_verify($signed_jwt, $_SESSION['provider']['client_secret']);
        } elseif (substr($header['alg'], 0, 2) == 'RS') {
            $pubkeys = array();
            if ($_SESSION['provider']['jwks_uri']) {
                $pubkeys['jku'] = $_SESSION['provider']['jwks_uri'];
            }
            $verified = jwt_verify($signed_jwt, $pubkeys);
        } elseif ($header['alg'] == 'none') {
            $verified = true;
        }
        log_info("Signature Verification = %d", $verified);
        if ($verified) {
            if (isset($payload['address']) && is_array($payload['address'])) {
                if (isset($payload['address']['formatted'])) {
                    $payload['address'] = $payload['address']['formatted'];
                } else {
                    $payload['address'] = "{$payload['address']['street_address']}\n{$payload['address']['locality']}, {$payload['address']['region']} {$payload['address']['postal_code']}\n{$payload['address']['country']}";
                }
            }
            if (isset($payload['aud']) && is_array($payload['aud'])) {
                $payload['aud'] = implode(', ', $payload['aud']);
            }
            $g_id_response = $payload;
            $g_info .= "ID Token Signature Verified\n";
            $_SESSION['id_token'] = $signed_jwt;
        } else {
            $g_info .= "ID Token Signature Verification Failed\n";
        }
    }
    return $response;
}