Changeset View
Changeset View
Standalone View
Standalone View
src/applications/auth/factor/PhabricatorTOTPAuthFactor.php
<?php | <?php | ||||
final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | final class PhabricatorTOTPAuthFactor extends PhabricatorAuthFactor { | ||||
const DIGEST_TEMPORARY_KEY = 'mfa.totp.sync'; | |||||
public function getFactorKey() { | public function getFactorKey() { | ||||
return 'totp'; | return 'totp'; | ||||
} | } | ||||
public function getFactorName() { | public function getFactorName() { | ||||
return pht('Mobile Phone App (TOTP)'); | return pht('Mobile Phone App (TOTP)'); | ||||
} | } | ||||
Show All 16 Lines | if (strlen($key)) { | ||||
// If the user is providing a key, make sure it's a key we generated. | // If the user is providing a key, make sure it's a key we generated. | ||||
// This raises the barrier to theoretical attacks where an attacker might | // This raises the barrier to theoretical attacks where an attacker might | ||||
// provide a known key (such attacks are already prevented by CSRF, but | // provide a known key (such attacks are already prevented by CSRF, but | ||||
// this is a second barrier to overcome). | // this is a second barrier to overcome). | ||||
// (We store and verify the hash of the key, not the key itself, to limit | // (We store and verify the hash of the key, not the key itself, to limit | ||||
// how useful the data in the table is to an attacker.) | // how useful the data in the table is to an attacker.) | ||||
$token_code = PhabricatorHash::digestWithNamedKey( | |||||
$key, | |||||
self::DIGEST_TEMPORARY_KEY); | |||||
$temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) | $temporary_token = id(new PhabricatorAuthTemporaryTokenQuery()) | ||||
->setViewer($user) | ->setViewer($user) | ||||
->withTokenResources(array($user->getPHID())) | ->withTokenResources(array($user->getPHID())) | ||||
->withTokenTypes(array($totp_token_type)) | ->withTokenTypes(array($totp_token_type)) | ||||
->withExpired(false) | ->withExpired(false) | ||||
->withTokenCodes(array(PhabricatorHash::weakDigest($key))) | ->withTokenCodes(array($token_code)) | ||||
->executeOne(); | ->executeOne(); | ||||
if (!$temporary_token) { | if (!$temporary_token) { | ||||
// If we don't have a matching token, regenerate the key below. | // If we don't have a matching token, regenerate the key below. | ||||
$key = null; | $key = null; | ||||
} | } | ||||
} | } | ||||
if (!strlen($key)) { | if (!strlen($key)) { | ||||
$key = self::generateNewTOTPKey(); | $key = self::generateNewTOTPKey(); | ||||
// Mark this key as one we generated, so the user is allowed to submit | // Mark this key as one we generated, so the user is allowed to submit | ||||
// a response for it. | // a response for it. | ||||
$token_code = PhabricatorHash::digestWithNamedKey( | |||||
$key, | |||||
self::DIGEST_TEMPORARY_KEY); | |||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); | ||||
id(new PhabricatorAuthTemporaryToken()) | id(new PhabricatorAuthTemporaryToken()) | ||||
->setTokenResource($user->getPHID()) | ->setTokenResource($user->getPHID()) | ||||
->setTokenType($totp_token_type) | ->setTokenType($totp_token_type) | ||||
->setTokenExpires(time() + phutil_units('1 hour in seconds')) | ->setTokenExpires(time() + phutil_units('1 hour in seconds')) | ||||
->setTokenCode(PhabricatorHash::weakDigest($key)) | ->setTokenCode($token_code) | ||||
->save(); | ->save(); | ||||
unset($unguarded); | unset($unguarded); | ||||
} | } | ||||
$code = $request->getStr('totpcode'); | $code = $request->getStr('totpcode'); | ||||
$e_code = true; | $e_code = true; | ||||
if ($request->getExists('totp')) { | if ($request->getExists('totp')) { | ||||
▲ Show 20 Lines • Show All 237 Lines • Show Last 20 Lines |