} elseif ($path_info == '/distributedinfo') {
        handle_distributedinfo();
    } elseif ($path_info == '/login') {
        handle_login();
    } elseif ($path_info == '/oplogin') {
        echo loginform('', '', null, true);
    } elseif ($path_info == '/confirm_userinfo') {
        handle_confirm_userinfo();
    } elseif ($path_info == '/registration') {
        handle_client_registration();
    } elseif (strpos($path_info, '/client') !== false) {
        handle_client_operations();
    } elseif ($path_info == '/endsession') {
        handle_end_session();
    } elseif ($path_info == '/logout') {
        handle_logout();
    } elseif ($path_info == '/proxy/done') {
        handle_proxy();
    } else {
        handle_default($path_info);
    }
}
exit;
/**
 * Show Login form.
 * @return String HTML Login form.
 */
function loginform($display_name = '', $user_id = '', $client = null, $oplogin = false)
{
    if ($display_name && $user_id) {
        $userid_field = " <b>{$display_name}</b><input type='hidden' name='username_display' value='{$display_name}'><input type='hidden' name='username' value='{$user_id}'><br/>";
function handle_start()
{
    global $g_error;
    $update = false;
    if ($_REQUEST['submit'] == 'Logout') {
        handle_logout();
        exit;
    }
    unset($_SESSION['id_token']);
    unset($_SESSION['session_state']);
    unset($_SESSION['code_verifier']);
    remember_session_form_options($_REQUEST);
    log_debug("handle_start : %s", print_r($_REQUEST, true));
    $provider = $_REQUEST['provider'];
    $identifier = $_REQUEST['identifier'];
    if (!$provider && !$identifier) {
        $g_error .= "No Identity Provider";
        return;
    }
    if ($identifier) {
        $discovery = webfinger_get_provider_info($identifier);
        if (!$discovery) {
            $g_error .= "Unable to perform discovery";
            return;
        }
        if (!check_client_update_options($_REQUEST, $discovery)) {
            return;
        }
        $db_provider = db_get_provider_by_url($discovery['url']);
        $provider = $db_provider;
        if (!$provider || !(isset($provider['client_id']) && isset($provider['client_secret']))) {
            if (!isset($discovery['registration_endpoint'])) {
                $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint";
                return;
            }
            $client_options = get_update_options($_REQUEST);
            $client_info = register_client($discovery['registration_endpoint'], $client_options);
            if (!$client_info) {
                $g_error .= "Unable to register client";
                return;
            }
            $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options);
            db_save_provider($discovery['issuer'], $provider);
            $provider = array_merge($provider, $client_info);
            $provider = array_merge($provider, $discovery);
        } else {
            $provider->delete();
            if (!isset($discovery['registration_endpoint'])) {
                $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint";
                return;
            }
            $client_options = get_update_options($_REQUEST);
            $client_info = register_client($discovery['registration_endpoint'], $client_options);
            if (!$client_info) {
                $g_error .= "Unable to register client";
                return;
            }
            $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options);
            db_save_provider($discovery['issuer'], $provider);
            $provider = array_merge($provider, $client_info);
            $provider = array_merge($provider, $discovery);
        }
    } elseif ($provider) {
        $db_provider = db_get_provider($provider);
        if (!$db_provider) {
            $g_error .= "Unregistered Identity Provider";
            return;
        }
        $p_info = $db_provider->toArray();
        if ($p_info['authorization_endpoint']) {
            $provider = $p_info;
            if (!$provider['client_id'] || !$provider['client_secret']) {
                $client_options = get_update_options($_REQUEST);
                log_debug('update options = %s', print_r($client_options, true));
                $client_info = register_client($provider['registration_endpoint'], $client_options);
                if (!$client_info) {
                    $g_error .= "Unable to register client";
                    return;
                }
                $provider = array_merge(array('client_id' => $client_info['client_id'], 'client_secret' => $client_info['client_secret']), $client_options);
                db_save_provider($db_provider['name'], $provider);
            }
            if ($p_info['name'] == RP_PROTOCOL . 'self-issued.me') {
                $provider['client_id'] = RP_REDIRECT_URI;
                $provider['client_secret'] = '';
            }
        } else {
            $provider_url = $p_info['url'];
            log_info("Provider URL = %s", $provider_url);
            $discovery = webfinger_get_provider_info($p_info['url']);
            if (!check_client_update_options($_REQUEST, $discovery)) {
                return;
            }
            if (!isset($discovery['registration_endpoint'])) {
                $g_error .= "Provider not found in db for {$discovery['issuer']} and no registration endpoint";
                return;
            }
            $client_options = get_update_options($_REQUEST);
            $client_info = register_client($discovery['registration_endpoint'], $client_options);
            log_debug('update options = %s', print_r($client_options, true));
            if (!$client_info) {
                $g_error .= "Unable to register client";
                return;
            }
            $provider = array_merge(array('name' => $discovery['issuer'], 'url' => $discovery['url'], 'issuer' => $discovery['issuer'], 'client_id' => $client_info['client_id'], 'client_id_issued_at' => $client_info['client_id_issued_at'], 'client_secret' => $client_info['client_secret'], 'registration_access_token' => $client_info['registration_access_token'], 'registration_client_uri' => $client_info['registration_client_uri'], 'client_secret_expires_at' => $client_info['client_secret_expires_at']), $client_options);
            $db_provider->delete();
            db_save_provider($discovery['issuer'], $provider);
            $provider = array_merge($provider, $client_info);
            $provider = array_merge($provider, $discovery);
        }
    }
    $_SESSION['provider'] = $provider;
    log_debug('final provider info %s', print_r($provider, true));
    $state = bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM));
    $nonce = bin2hex(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM));
    $response_type = '';
    if ($_REQUEST['response_type']) {
        $response_type = $_REQUEST['response_type'];
    }
    if (!$response_type) {
        if ($_REQUEST['response_type1']) {
            $response_type = $_REQUEST['response_type1'];
        }
    }
    if (!$response_type) {
        $g_error = 'No response type';
        return;
    }
    $query_params = array('state' => $state, 'redirect_uri' => RP_REDIRECT_URI, 'response_type' => $response_type, 'client_id' => $provider['client_id'], 'nonce' => $nonce);
    $scope_types = array('openid', 'profile', 'email', 'address', 'phone', 'offline_access');
    $scopes = array();
    foreach ($scope_types as $scope_type) {
        $param_name = 'scope_' . $scope_type;
        if ($_REQUEST[$param_name] == 'on') {
            $scopes[] = $scope_type;
        }
    }
    if (isset($provider['scopes_supported'])) {
        $provider_scopes = $provider['scopes_supported'];
        if (!is_array($provider_scopes)) {
            $provider_scopes = explode(' ', $provider_scopes);
        }
        log_debug('provider scopes = %s', print_r($provider_scopes, true));
        $diff = array_diff($provider_scopes, $scope_types);
        log_debug('diff = %s', print_r($diff, true));
        if (isset($db_provider) && isset($db_provider['authorization_endpoint'])) {
            $provider_scopes = $diff;
        } else {
            $provider_scopes = array();
        }
    } else {
        $provider_scopes = array();
    }
    $unique_scopes = array_unique(array_merge($scopes, $provider_scopes), SORT_STRING);
    $query_params['scope'] = implode(' ', $unique_scopes);
    log_debug('scopes = %s', print_r($query_params['scope'], true));
    $code_verifier = base64url_encode(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
    $_SESSION['code_verifier'] = $code_verifier;
    $code_challenge = base64url_encode(hash('sha256', $code_verifier, true));
    $query_params['code_challenge_method'] = 'S256';
    $query_params['code_challenge'] = $code_challenge;
    log_debug("code verifier : %s challenge : %s method : %s", $code_verifier, $code_challenge, $query_params['code_challenge_method']);
    if ($_REQUEST['response_mode']) {
        $query_params['response_mode'] = $_REQUEST['response_mode'];
    }
    if ($_REQUEST['page']) {
        $query_params['page'] = $_REQUEST['page'];
    }
    $prompt_types = array('none', 'login', 'consent', 'select_account');
    $prompt = array();
    foreach ($prompt_types as $prompt_type) {
        $param_name = 'prompt_' . $prompt_type;
        if ($_REQUEST[$param_name] == 'on') {
            $prompt[] = $prompt_type;
        }
    }
    if (count($prompt)) {
        $query_params['prompt'] = implode(' ', $prompt);
    }
    if ($_REQUEST['id_token']) {
        $query_params['id_token_hint'] = $_REQUEST['id_token'];
    }
    if ($_REQUEST['login_hint']) {
        $query_params['login_hint'] = $_REQUEST['login_hint'];
    }
    $custom_params = array();
    if ($_REQUEST['request_option']) {
        //        $custom_query = array();
        if (strstr($_REQUEST['request_option'], 'Custom') !== false) {
            switch ($_REQUEST['request_option']) {
                case 'Custom 19':
                    $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true)));
                    break;
                case 'Custom 20':
                    $custom_params['claims'] = array('userinfo' => array('email' => NULL, 'picture' => NULL));
                    break;
                case 'Custom 21':
                    $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'email' => NULL, 'picture' => NULL));
                    break;
                case 'Custom 22':
                    $custom_params['claims'] = array('id_token' => array('auth_time' => array('essential' => true)));
                    break;
                case 'Custom 23':
                    $custom_params['claims'] = array('id_token' => array('acr' => array('values' => array('0', '1', '2'), 'essential' => true)));
                    break;
                case 'Custom 24':
                    $custom_params['claims'] = array('id_token' => array('acr' => array('values' => array('0', '1', '2'))));
                    break;
                case 'Custom 25a':
                    $query_params['max_age'] = 1;
                    break;
                case 'Custom 25b':
                    $query_params['max_age'] = 10;
                    break;
                case 'Custom Dist':
                    $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'email' => NULL, 'picture' => NULL, 'undergrad_school' => array('essential' => true), 'graduate_school' => array('essential' => true), 'undergrad_degrees' => array('essential' => true), 'graduate_degrees' => array('essential' => true)));
                    break;
                case 'Custom Req 1':
                    $query_params['max_age'] = 1 * 60;
                    $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true), 'given_name' => array('essential' => true), 'family_name' => array('essential' => true), 'middle_name' => array('essential' => true), 'address' => array('essential' => true), 'nickname' => NULL, 'profile' => NULL, 'given_name#ja-Kana-JP' => NULL, 'given_name#ja_Hani-JP' => NULL), 'id_token' => array('given_name' => array('essential' => true), 'family_name' => array('essential' => true), 'email' => array('essential' => true), 'gender' => array('essential' => true), 'address' => array('essential' => true), 'auth_time' => array('essential' => true)));
                    break;
                case 'Custom Req 2':
                    $query_params['max_age'] = 1 * 60;
                    $custom_params['claims'] = array('userinfo' => array('name' => array('essential' => true)), 'id_token' => array('auth_time' => array('essential' => true)));
                    break;
                default:
                    break;
            }
        }
    }
    $request_method = $_REQUEST['request_method'];
    if ($_REQUEST['request_option']) {
        if ($request_method == 'GET') {
            if (count($custom_params)) {
                $query_params['claims'] = json_encode($custom_params['claims']);
            }
        } else {
            if (strstr($request_method, 'Request File') !== false) {
                $fileid = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
                // $query_params['request_uri'] = RP_INDEX_PAGE . "/reqfile?fileid={$fileid}";
            } else {
                $fileid = NULL;
            }
            $sig_param = array('alg' => 'none');
            if ($_REQUEST['request_object_signing_alg']) {
                $sig_param['alg'] = $_REQUEST['request_object_signing_alg'];
            }
            if (substr($sig_param['alg'], 0, 2) == 'HS') {
                $sig_key = $provider['client_secret'];
            } elseif (substr($sig_param['alg'], 0, 2) == 'RS') {
                $sig_param['jku'] = RP_JWK_URL;
                $sig_param['kid'] = RP_SIG_KID;
                $sig_key = array('key_file' => RP_SIG_PKEY, 'password' => RP_SIG_PKEY_PASSPHRASE);
            }
            log_debug("Request Object Using Sig Alg %s", $sig_param['alg']);
            $request_jwt = jwt_sign(array_merge(array_diff_key($query_params, array('id_token' => 0)), $custom_params), $sig_param, $sig_key);
            if (!$request_jwt) {
                $g_error .= 'Unable to sign request object';
                return;
            }
            if ($_REQUEST['request_object_encrypted_response_alg'] && $_REQUEST['request_object_encrypted_response_enc']) {
                $supported_cek_algs = array('RSA1_5', 'RSA-OAEP');
                $supported_plaintext_algs = array('A128GCM', 'A256GCM', 'A128CBC-HS256', 'A256CBC-HS512');
                if (!in_array($_REQUEST['request_object_encrypted_response_alg'], $supported_cek_algs)) {
                    $g_error .= "Unsupported Request Object CEK Alg";
                    return;
                }
                if (!in_array($_REQUEST['request_object_encrypted_response_enc'], $supported_plaintext_algs)) {
                    $g_error .= "Unsupported Request Object Plaintext Alg";
                    return;
                }
                $jwk_uri = '';
                $encryption_keys = NULL;
                if ($provider['jwks_uri']) {
                    $jwk = get_url($provider['jwks_uri']);
                    if ($jwk) {
                        $jwk_uri = $provider['jwks_uri'];
                        $encryption_keys = jwk_get_keys($jwk, 'RSA', 'enc', NULL);
                        if (!$encryption_keys || !count($encryption_keys)) {
                            $encryption_keys = NULL;
                        }
                    }
                }
                if (!$encryption_keys) {
                    $g_error .= 'No JWK key for encryption';
                    return NULL;
                }
                $header_params = array('jku' => $jwk_uri);
                if (isset($encryption_keys[0]['kid'])) {
                    $header_params['kid'] = $encryption_keys[0]['kid'];
                }
                $encrypted_jwt = jwt_encrypt2($request_jwt, $encryption_keys[0], false, NULL, $header_params, NULL, $_REQUEST['request_object_encrypted_response_alg'], $_REQUEST['request_object_encrypted_response_enc'], false);
                if (!$encrypted_jwt) {
                    $g_error .= "Unable to encrypt request object.";
                    return;
                } else {
                    $request_jwt = $encrypted_jwt;
                }
            } else {
                //            $custom_query['request'] = $request_jwt;
            }
            // if(isset($query_params['request_uri'])) { // save file to db
            if (isset($fileid)) {
                // save file to db
                $query_params['request_uri'] = RP_INDEX_PAGE . "/reqfile?fileid={$fileid}";
                $reqfile = array('type' => $encrypted_jwt ? 1 : 0, 'request' => json_encode(array_merge($query_params, $custom_params)), 'jwt' => $request_jwt);
                log_debug('query_params = %s custom = %s', print_r($query_params, true), print_r($custom_params, true));
                db_save_request_file($fileid, $reqfile);
            } else {
                $query_params['request'] = $request_jwt;
            }
        }
    }
    $url = $provider['authorization_endpoint'] . '?' . http_build_query($query_params);
    log_info("redirect to %s", $url);
    header("Location: {$url}");
    exit;
}