3 * @copyright Copyright (C) 2010-2022, the Friendica project
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Model\User;
25 use Friendica\Core\Config\Capability\IManageConfigValues;
28 * Interacting with the Friendica Cookie of a user
32 /** @var int Default expire duration in days */
33 const DEFAULT_EXPIRE = 7;
34 /** @var string The name of the Friendica cookie */
35 const NAME = 'Friendica';
36 /** @var string The path of the Friendica cookie */
38 /** @var string The domain name of the Friendica cookie */
40 /** @var bool True, if the cookie should only be accessible through HTTP */
41 const HTTPONLY = true;
43 /** @var string The remote address of this node */
45 /** @var bool True, if the connection is ssl enabled */
47 /** @var string The private key of this Friendica node */
48 private $sitePrivateKey;
49 /** @var int The default cookie lifetime */
51 /** @var array The Friendica cookie data array */
55 * @param App\Request $request The current http request
56 * @param IManageConfigValues $config
57 * @param App\BaseURL $baseURL
58 * @param array $COOKIE The $_COOKIE array
60 public function __construct(App\Request $request, IManageConfigValues $config, App\BaseURL $baseURL, array $COOKIE = [])
62 $this->sslEnabled = $baseURL->getSSLPolicy() === App\BaseURL::SSL_POLICY_FULL;
63 $this->sitePrivateKey = $config->get('system', 'site_prvkey');
65 $authCookieDays = $config->get('system', 'auth_cookie_lifetime',
66 self::DEFAULT_EXPIRE);
67 $this->lifetime = $authCookieDays * 24 * 60 * 60;
69 $this->remoteAddr = $request->getRemoteAddress();
71 $this->data = json_decode($COOKIE[self::NAME] ?? '[]', true) ?: [];
75 * Returns the value for a key of the Friendica cookie
78 * @param mixed $default
79 * @return mixed|null The value for the provided cookie key
81 public function get(string $key, $default = null)
83 return $this->data[$key] ?? $default;
87 * Set a single cookie key value.
88 * Overwrites an existing value with the same key.
94 public function set($key, $value): bool
96 return $this->setMultiple([$key => $value]);
100 * Sets multiple cookie key values.
101 * Overwrites existing values with the same key.
103 * @param array $values
106 public function setMultiple(array $values): bool
108 $this->data = $values + $this->data;
110 return $this->send();
114 * Remove a cookie key
118 public function unset(string $key)
120 if (isset($this->data[$key])) {
121 unset($this->data[$key]);
128 * Resets the cookie to a given data set
134 public function reset(array $data): bool
136 return $this->clear() &&
137 $this->setMultiple($data);
141 * Clears the Friendica cookie
143 public function clear(): bool
146 // make sure cookie is deleted on browser close, as a security measure
147 return $this->setCookie('', -3600, $this->sslEnabled);
151 * Send the cookie, should be called every time $this->data is changed or to refresh the cookie.
155 public function send(): bool
157 return $this->setCookie(
158 json_encode(['ip' => $this->remoteAddr] + $this->data),
159 $this->lifetime + time(),
165 * setcookie() wrapper: protected, internal function for test-mocking possibility
167 * @link https://php.net/manual/en/function.setcookie.php
169 * @param string $value [optional]
170 * @param int $expire [optional]
171 * @param bool $secure [optional]
173 * @return bool If output exists prior to calling this function,
176 protected function setCookie(string $value = null, int $expire = null,
177 bool $secure = null): bool
179 return setcookie(self::NAME, $value, $expire, self::PATH, self::DOMAIN, $secure, self::HTTPONLY);
183 * Calculate a hash of a user's private data for storage in the cookie.
184 * Hashed twice, with the user's own private key first, then the node's private key second.
186 * @param string $privateData User private data
187 * @param string $privateKey User private key
189 * @return string Hashed data
191 public function hashPrivateData(string $privateData, string $privateKey): string
195 hash_hmac('sha256', $privateData, $privateKey),
196 $this->sitePrivateKey
201 * @param string $hash Hash from a cookie key value
202 * @param string $privateData User private data
203 * @param string $privateKey User private key
208 public function comparePrivateDataHash(string $hash, string $privateData, string $privateKey): bool
211 $this->hashPrivateData($privateData, $privateKey),