From 6fa9f1fed29f806651b90fd823b468854a19275a Mon Sep 17 00:00:00 2001 From: Eldad Fux Date: Tue, 17 Mar 2020 13:36:13 +0200 Subject: [PATCH] Disabled cookie fallback when domain is verified --- app/app.php | 8 +++++- app/controllers/api/account.php | 45 +++++++++++++++++++++++++-------- app/controllers/api/teams.php | 9 +++++-- app/init.php | 1 + 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/app/app.php b/app/app.php index 52fc0816f..ff918bb56 100644 --- a/app/app.php +++ b/app/app.php @@ -16,6 +16,7 @@ use Database\Database; use Database\Document; use Database\Validator\Authorization; use Event\Event; +use Utopia\Domains\Domain; use Utopia\Validator\WhiteList; /* @@ -52,7 +53,7 @@ $clients = array_unique(array_merge($clientsConsole, array_map(function ($node) return false; })))); -$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $roles, $webhook, $audit, $usage, $domain, $clients) { +$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $roles, $webhook, $audit, $usage, $domain, $clients, &$domainVerification) { $route = $utopia->match($request); @@ -64,6 +65,11 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $ $refDomain = $protocol.'://'.((in_array($origin, $clients)) ? $origin : 'localhost') . (!empty($port) ? ':'.$port : ''); + $selfDomain = new Domain($domain); + $endDomain = new Domain($origin); + + $domainVerification = ($selfDomain->getRegisterable() === $endDomain->getRegisterable()); + /* * Security Headers * diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index fea9cefcb..b40fb5a26 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -150,7 +150,7 @@ $utopia->post('/v1/account/sessions') ->param('email', '', function () { return new Email(); }, 'User email.') ->param('password', '', function () { return new Password(); }, 'User password.') ->action( - function ($email, $password) use ($response, $request, $projectDB, $audit, $webhook, $protocol) { + function ($email, $password) use ($response, $request, $projectDB, $audit, $webhook, $protocol, $domainVerification) { $profile = $projectDB->getCollection([ // Get user by email address 'limit' => 1, 'first' => true, @@ -210,11 +210,16 @@ $utopia->post('/v1/account/sessions') ->setParam('event', 'account.sessions.create') ->setParam('resource', 'users/'.$profile->getId()) ; + + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)])) + ; + } $response ->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)])) ->setStatusCode(Response::STATUS_CODE_CREATED) ->json($session->getArrayCopy(['$id', 'type', 'expire'])) ; @@ -294,7 +299,7 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect') ->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.') ->param('state', '', function () { return new Text(2048); }, 'OAuth2 state params.', true) ->action( - function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit, $protocol) { + function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit, $protocol, $domainVerification) { $callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId(); $defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => '']; $validateURL = new URL(); @@ -443,10 +448,15 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect') ->setParam('data', ['provider' => $provider]) ; + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) + ; + } + $response ->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) ->redirect($state['success']) ; } @@ -812,7 +822,7 @@ $utopia->delete('/v1/account') ->label('sdk.method', 'delete') ->label('sdk.description', '/docs/references/account/delete.md') ->action( - function () use ($response, $user, $projectDB, $audit, $webhook, $protocol) { + function () use ($response, $user, $projectDB, $audit, $webhook, $protocol, $domainVerification) { $user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [ 'status' => Auth::USER_STATUS_BLOCKED, ])); @@ -843,10 +853,15 @@ $utopia->delete('/v1/account') ]) ; + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([])) + ; + } + $response ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([])) ->noContent() ; } @@ -863,7 +878,7 @@ $utopia->delete('/v1/account/sessions/:sessionId') ->label('abuse-limit', 100) ->param('sessionId', null, function () { return new UID(); }, 'Session unique ID. Use the string \'current\' to delete the current device session.') ->action( - function ($sessionId) use ($response, $user, $projectDB, $webhook, $audit, $protocol) { + function ($sessionId) use ($response, $user, $projectDB, $webhook, $audit, $protocol, $domainVerification) { $sessionId = ($sessionId === 'current') ? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret) : $sessionId; @@ -889,11 +904,16 @@ $utopia->delete('/v1/account/sessions/:sessionId') ]) ; + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([])) + ; + } + if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $response ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([])) ; } @@ -915,7 +935,7 @@ $utopia->delete('/v1/account/sessions') ->label('sdk.description', '/docs/references/account/delete-sessions.md') ->label('abuse-limit', 100) ->action( - function () use ($response, $user, $projectDB, $audit, $webhook, $protocol) { + function () use ($response, $user, $projectDB, $audit, $webhook, $protocol, $domainVerification) { $tokens = $user->getAttribute('tokens', []); foreach ($tokens as $token) { /* @var $token Document */ @@ -936,11 +956,16 @@ $utopia->delete('/v1/account/sessions') ]) ; + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([])) + ; + } + if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too $response ->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([])) ; } } diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 0897f7414..6aaf64ab1 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -426,7 +426,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status') ->param('userId', '', function () { return new UID(); }, 'User unique ID.') ->param('secret', '', function () { return new Text(256); }, 'Secret key.') ->action( - function ($teamId, $inviteId, $userId, $secret) use ($response, $request, $user, $audit, $projectDB, $protocol) { + function ($teamId, $inviteId, $userId, $secret) use ($response, $request, $user, $audit, $projectDB, $protocol, $domainVerification) { $membership = $projectDB->getDocument($inviteId); if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) { @@ -520,10 +520,15 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status') ->setParam('resource', 'teams/'.$teamId) ; + if(!$domainVerification) { + $response + ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) + ; + } + $response ->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null) ->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE) - ->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)])) ->json(array_merge($membership->getArrayCopy([ '$id', 'userId', diff --git a/app/init.php b/app/init.php index e700c2f2f..c7bf6e7cc 100644 --- a/app/init.php +++ b/app/init.php @@ -48,6 +48,7 @@ $response = new Response(); */ $env = $request->getServer('_APP_ENV', App::ENV_TYPE_PRODUCTION); $domain = $request->getServer('HTTP_HOST', ''); +$domainVerification = false; $version = $request->getServer('_APP_VERSION', 'UNKNOWN'); $providers = include __DIR__.'/../app/config/providers.php'; // OAuth2 providers list $platforms = include __DIR__.'/../app/config/platforms.php';