4 * Consolidates Phabricator application cookies, including registration
5 * and session management.
7 * @task clientid Client ID Cookie
8 * @task next Next URI Cookie
10 final class PhabricatorCookies
extends Phobject
{
13 * Stores the login username for password authentication. This is just a
14 * display value for convenience, used to prefill the login form. It is not
17 const COOKIE_USERNAME
= 'phusr';
21 * Stores the user's current session ID. This is authoritative and establishes
22 * the user's identity.
24 const COOKIE_SESSION
= 'phsid';
28 * Stores a secret used during new account registration to prevent an attacker
29 * from tricking a victim into registering an account which is linked to
30 * credentials the attacker controls.
32 const COOKIE_REGISTRATION
= 'phreg';
36 * Stores a secret used during OAuth2 handshakes to prevent various attacks
37 * where an attacker hands a victim a URI corresponding to the middle of an
38 * OAuth2 workflow and we might otherwise do something sketchy. Particularly,
39 * this corresponds to the OAuth2 "code".
41 const COOKIE_CLIENTID
= 'phcid';
45 * Stores the URI to redirect the user to after login. This allows users to
46 * visit a path like `/feed/`, be prompted to login, and then be redirected
47 * back to `/feed/` after the workflow completes.
49 const COOKIE_NEXTURI
= 'next_uri';
53 * Stores a hint that the user should be moved directly into high security
54 * after upgrading a partial login session. This is used during password
55 * recovery to avoid a double-prompt.
57 const COOKIE_HISEC
= 'jump_to_hisec';
61 * Stores an invite code.
63 const COOKIE_INVITE
= 'invite';
67 * Stores a workflow completion across a redirect-after-POST following a
68 * form submission. This can be used to show "Changes Saved" messages.
70 const COOKIE_SUBMIT
= 'phfrm';
73 /* -( Client ID Cookie )--------------------------------------------------- */
77 * Set the client ID cookie. This is a random cookie used like a CSRF value
78 * during authentication workflows.
80 * @param AphrontRequest Request to modify.
84 public static function setClientIDCookie(AphrontRequest
$request) {
86 // NOTE: See T3471 for some discussion. Some browsers and browser extensions
87 // can make duplicate requests, so we overwrite this cookie only if it is
88 // not present in the request. The cookie lifetime is limited by making it
89 // temporary and clearing it when users log out.
91 $value = $request->getCookie(self
::COOKIE_CLIENTID
);
92 if (!strlen($value)) {
93 $request->setTemporaryCookie(
94 self
::COOKIE_CLIENTID
,
95 Filesystem
::readRandomCharacters(16));
100 /* -( Next URI Cookie )---------------------------------------------------- */
104 * Set the Next URI cookie. We only write the cookie if it wasn't recently
105 * written, to avoid writing over a real URI with a bunch of "humans.txt"
106 * stuff. See T3793 for discussion.
108 * @param AphrontRequest Request to write to.
109 * @param string URI to write.
110 * @param bool Write this cookie even if we have a fresh
116 public static function setNextURICookie(
117 AphrontRequest
$request,
122 $cookie_value = $request->getCookie(self
::COOKIE_NEXTURI
);
123 list($set_at, $current_uri) = self
::parseNextURICookie($cookie_value);
125 // If the cookie was set within the last 2 minutes, don't overwrite it.
126 // Primarily, this prevents browser requests for resources which do not
127 // exist (like "humans.txt" and various icons) from overwriting a normal
128 // URI like "/feed/".
129 if ($set_at > (time() - 120)) {
134 $new_value = time().','.$next_uri;
135 $request->setTemporaryCookie(self
::COOKIE_NEXTURI
, $new_value);
140 * Read the URI out of the Next URI cookie.
142 * @param AphrontRequest Request to examine.
143 * @return string|null Next URI cookie's URI value.
147 public static function getNextURICookie(AphrontRequest
$request) {
148 $cookie_value = $request->getCookie(self
::COOKIE_NEXTURI
);
149 list($set_at, $next_uri) = self
::parseNextURICookie($cookie_value);
156 * Parse a Next URI cookie into its components.
158 * @param string Raw cookie value.
159 * @return list<string> List of timestamp and URI.
163 private static function parseNextURICookie($cookie) {
164 // Old cookies look like: /uri
165 // New cookies look like: timestamp,/uri
167 if (!strlen($cookie)) {
171 if (strpos($cookie, ',') !== false) {
172 list($timestamp, $uri) = explode(',', $cookie, 2);
173 return array((int)$timestamp, $uri);
176 return array(0, $cookie);