Allow using an app token to login with v2 flow auth

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl 2021-11-03 10:54:02 +01:00
parent 8c3ab46545
commit aa3f4bdf63
No known key found for this signature in database
GPG Key ID: 4C614C6ED2CDE6DF
4 changed files with 82 additions and 0 deletions

View File

@ -27,6 +27,7 @@ declare(strict_types=1);
*/
namespace OC\Core\Controller;
use OC\Authentication\Exceptions\InvalidTokenException;
use OC\Core\Db\LoginFlowV2;
use OC\Core\Exception\LoginFlowV2NotFoundException;
use OC\Core\Service\LoginFlowV2Service;
@ -173,6 +174,48 @@ class ClientFlowLoginV2Controller extends Controller {
);
}
/**
* @PublicPage
*/
public function apptokenRedirect(string $stateToken, string $user, string $password) {
if (!$this->isValidStateToken($stateToken)) {
return $this->stateTokenForbiddenResponse();
}
try {
$this->getFlowByLoginToken();
} catch (LoginFlowV2NotFoundException $e) {
return $this->loginTokenForbiddenResponse();
}
$loginToken = $this->session->get(self::TOKEN_NAME);
// Clear session variables
$this->session->remove(self::TOKEN_NAME);
$this->session->remove(self::STATE_NAME);
try {
$token = \OC::$server->get(\OC\Authentication\Token\IProvider::class)->getToken($password);
if ($token->getLoginName() !== $user) {
throw new InvalidTokenException('login name does not match');
}
} catch (InvalidTokenException $e) {
$response = new StandaloneTemplateResponse(
$this->appName,
'403',
[
'message' => $this->l10n->t('Invalid app password'),
],
'guest'
);
$response->setStatus(Http::STATUS_FORBIDDEN);
return $response;
}
$result = $this->loginFlowV2Service->flowDoneWithAppPassword($loginToken, $this->getServerPath(), $this->userId, $password);
return $this->handleFlowDone($result);
}
/**
* @NoAdminRequired
* @UseSession
@ -196,7 +239,10 @@ class ClientFlowLoginV2Controller extends Controller {
$sessionId = $this->session->getId();
$result = $this->loginFlowV2Service->flowDone($loginToken, $sessionId, $this->getServerPath(), $this->userId);
return $this->handleFlowDone($result);
}
private function handleFlowDone(bool $result): StandaloneTemplateResponse {
if ($result) {
return new StandaloneTemplateResponse(
$this->appName,

View File

@ -186,6 +186,23 @@ class LoginFlowV2Service {
return true;
}
public function flowDoneWithAppPassword(string $loginToken, string $server, string $loginName, string $appPassword): bool {
try {
$data = $this->mapper->getByLoginToken($loginToken);
} catch (DoesNotExistException $e) {
return false;
}
$data->setLoginName($loginName);
$data->setServer($server);
// Properly encrypt
$data->setAppPassword($this->encryptPassword($appPassword, $data->getPublicKey()));
$this->mapper->update($data);
return true;
}
public function createTokens(string $userAgent): LoginFlowV2Tokens {
$flow = new LoginFlowV2();
$pollToken = $this->random->generate(128, ISecureRandom::CHAR_DIGITS.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER);

View File

@ -68,6 +68,7 @@ $application->registerRoutes($this, [
['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'],
['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'],
['name' => 'ClientFlowLoginV2#apptokenRedirect', 'url' => '/login/v2/apptoken', 'verb' => 'POST'],
['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],

View File

@ -20,6 +20,7 @@
*/
style('core', 'login/authpicker');
script('core', 'login/authpicker');
/** @var array $_ */
/** @var \OCP\IURLGenerator $urlGenerator */
@ -50,4 +51,21 @@ $urlGenerator = $_['urlGenerator'];
</a>
</p>
<form action="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLoginV2.apptokenRedirect')); ?>" method="post" id="app-token-login-field" class="hidden">
<p class="grouptop">
<input type="text" name="user" id="user" placeholder="<?php p($l->t('Username')) ?>">
<label for="user" class="infield"><?php p($l->t('Username')) ?></label>
</p>
<p class="groupbottom">
<input type="password" name="password" id="password" placeholder="<?php p($l->t('App token')) ?>">
<label for="password" class="infield"><?php p($l->t('Password')) ?></label>
</p>
<input type="hidden" name="stateToken" value="<?php p($_['stateToken']) ?>" />
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>">
<input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Grant access')) ?>">
</form>
</div>
<?php if (empty($_['oauthState'])): ?>
<a id="app-token-login" class="warning" href="#"><?php p($l->t('Alternative log in using app token')) ?></a>
<?php endif; ?>