<?php
/**
* api: php
* title: Session startup
* description: Avoids session startup until actual login occured
* license: MITL
* version: 0.4
*
* Start $_SESSION only if there's already a session cookie present.
* (Prevent needless cookies and tracking ids for not logged-in users.)
*
* The only handler that initiates any login process is `page_login.php`
*
* Now primarily using IndieAuth, albeit OpenID plugin remains active.
# And $_SESSION["openid"] will be kept throughout the codebase anywho.
*
*/
#error_reporting(E_ALL);set_error_handler("var_dump");
// Kill off CloudFlare cookie when Do-Not-Track header present
### WILL BECOME REDUNDANT IN MAY 2021 ###
if ($_SERVER->has("HTTP_DNT") and $_SERVER->boolean["HTTP_DNT"]) {
header("Set-Cookie: __cfduid= ; path=/; domain=.freshcode.club; HttpOnly");
}
define("INDIEAUTH_API", "https://indieauth.com/auth");
define("INDIEAUTH_CLIENT_ID", "https://freshcode.club/");
define("INDIEAUTH_REDIRECT", "https://freshcode.club/login");
// Check for pre-existant cookie before defaulting to initiate session store
if ($_COOKIE->has("USER") or $_REQUEST->has("set_password")) {
session_fresh();
}
// If there's none, make sure there's an array at least
if (!isset($_SESSION)) {
$_SESSION = ["fromempty1"=>1];
}
// Populate stub array with empty defaults.
isset($_SESSION["openid"]) or $_SESSION["openid"] = "" and $_SESSION["reset_3"] = "3";
isset($_SESSION["name"]) or $_SESSION["name"] = "";
isset($_SESSION["csrf"]) or $_SESSION["csrf"] = [];
isset($_SESSION["password"]) or $_SESSION["password"] = "";
// verify incoming OpenID request
if ($_GET->has("openid_mode") and empty($_SESSION["openid"])) {
try {
$openid = new LightOpenID(HTTP_HOST);
$openid->verify_peer = false;
if ($openid->mode) {
if ($openid->validate()) {
$_COOKIE->no("USER") and session_fresh();
$_SESSION["openid"] = $openid->identity;
$_SESSION["name"] = $openid->getAttributes()["namePerson/friendly"];
}
}
}
catch (ErrorException $e) {
die("OpenID verify exception (possibly endpoint / SSL error)");
}
}
elseif ($_REQUEST->has("set_password")) {
$_SESSION["password"] = $_REQUEST->ascii->nocontrol->trim["set_password"];
}
elseif ($_REQUEST->has("start_indieauth")) {
initiate_indieauth($_POST->uri["login_url"]);
}
elseif ($_REQUEST->has("code","me")) {
$indieauth_login = verify_indieauth();
}
#-- IndieAuth
function initiate_indieauth($url="") {
$_SESSION["indie/state"] = $_state = md5("ia:".rand());
die(header(
"Location: " . INDIEAUTH_API . "?me=" . urlencode($url) .
"&client_id=" . INDIEAUTH_CLIENT_ID . "&redirect_uri=" . INDIEAUTH_REDIRECT .
"&state=" . urlencode($_state)
));
}
#-- if &code= parameter received
function verify_indieauth() {
# "https://freshcode.club/login?code=...&me=http://userurl..."
$fields = [
"code" => $_REQUEST->raw["code"],
"client_id" => INDIEAUTH_CLIENT_ID,
"redirect_uri" => INDIEAUTH_REDIRECT,
];
$json = curl(INDIEAUTH_API)->httpheader(array("Accept: application/json"))->post(1)->postfields(
http_build_query($fields)
)->exec();
if ($json) {
$d = json_decode($json, True) or parse_str($json, $d);
if (!empty($d["me"])) {
session_fresh();
return $_SESSION["openid"] = $d["me"];
}
}
}
#session_write_close();
// Prevent some session tampering
function session_fresh() {
// Initiate with current session identifier
if ($_COOKIE->has("USER")) {
session_id($_COOKIE->id["USER"]);
}
session_name("USER");
session_set_cookie_params(7*24*3600, "/", HTTP_HOST, false, true);
# session_cache_limiter('private');
session_cache_expire(7*24*60);
session_start();
// Security by obscurity: lock client against User-Agent
$useragent = $_SERVER->text->lengthβ¦30["HTTP_USER_AGENT"];
// Security by obscurity: IP subnet lock (or just major route for IPv6)
$subnet = $_SERVER->ip->lengthβ¦6["REMOTE_ADDR"];
// Server-side timeout (7 days)
$expire = time() + 7 * 24 * 3600;
// New ID for mismatches
if (empty($_SESSION["state/client"]) or $_SESSION["state/client"] != $useragent
or empty($_SESSION["state/subnet"]) or $_SESSION["state/subnet"] != $subnet
or empty($_SESSION["state/expire"]) or $_SESSION["state/expire"] < time()
) {
$dat = json_encode($_SESSION);
# CloudFlare vs PHP 5.6.4
/*
#session_destroy();
#session_regenerate_id(true);
#session_start();
*/
// $_SESSION["destroyed2"] = "{$_COOKIE->json_encode['USER']} / $dat / $useragent / $subnet / " . time();
}
// and Repopulate status fields
$_SESSION["state/client"] = $useragent;
$_SESSION["state/subnet"] = $subnet;
$_SESSION["state/expire"] = $expire;
}