Skip to content

Commit ced51d7

Browse files
committed
Allow using an app token to login with v2 flow auth
Signed-off-by: Julius Härtl <jus@bitgrid.net>
1 parent ec4474c commit ced51d7

4 files changed

Lines changed: 82 additions & 0 deletions

File tree

core/Controller/ClientFlowLoginV2Controller.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
*/
2828
namespace OC\Core\Controller;
2929

30+
use OC\Authentication\Exceptions\InvalidTokenException;
3031
use OC\Core\Db\LoginFlowV2;
3132
use OC\Core\Exception\LoginFlowV2NotFoundException;
3233
use OC\Core\Service\LoginFlowV2Service;
@@ -173,6 +174,48 @@ public function grantPage(string $stateToken): StandaloneTemplateResponse {
173174
);
174175
}
175176

177+
/**
178+
* @PublicPage
179+
*/
180+
public function apptokenRedirect(string $stateToken, string $user, string $password) {
181+
if (!$this->isValidStateToken($stateToken)) {
182+
return $this->stateTokenForbiddenResponse();
183+
}
184+
185+
try {
186+
$this->getFlowByLoginToken();
187+
} catch (LoginFlowV2NotFoundException $e) {
188+
return $this->loginTokenForbiddenResponse();
189+
}
190+
191+
$loginToken = $this->session->get(self::TOKEN_NAME);
192+
193+
// Clear session variables
194+
$this->session->remove(self::TOKEN_NAME);
195+
$this->session->remove(self::STATE_NAME);
196+
197+
try {
198+
$token = \OC::$server->get(\OC\Authentication\Token\IProvider::class)->getToken($password);
199+
if ($token->getLoginName() !== $user) {
200+
throw new InvalidTokenException('login name does not match');
201+
}
202+
} catch (InvalidTokenException $e) {
203+
$response = new StandaloneTemplateResponse(
204+
$this->appName,
205+
'403',
206+
[
207+
'message' => $this->l10n->t('Invalid app password'),
208+
],
209+
'guest'
210+
);
211+
$response->setStatus(Http::STATUS_FORBIDDEN);
212+
return $response;
213+
}
214+
215+
$result = $this->loginFlowV2Service->flowDoneWithAppPassword($loginToken, $this->getServerPath(), $this->userId, $password);
216+
return $this->handleFlowDone($result);
217+
}
218+
176219
/**
177220
* @NoAdminRequired
178221
* @UseSession
@@ -196,7 +239,10 @@ public function generateAppPassword(string $stateToken): Response {
196239
$sessionId = $this->session->getId();
197240

198241
$result = $this->loginFlowV2Service->flowDone($loginToken, $sessionId, $this->getServerPath(), $this->userId);
242+
return $this->handleFlowDone($result);
243+
}
199244

245+
private function handleFlowDone(bool $result): StandaloneTemplateResponse {
200246
if ($result) {
201247
return new StandaloneTemplateResponse(
202248
$this->appName,

core/Service/LoginFlowV2Service.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,23 @@ public function flowDone(string $loginToken, string $sessionId, string $server,
186186
return true;
187187
}
188188

189+
public function flowDoneWithAppPassword(string $loginToken, string $server, string $loginName, string $appPassword): bool {
190+
try {
191+
$data = $this->mapper->getByLoginToken($loginToken);
192+
} catch (DoesNotExistException $e) {
193+
return false;
194+
}
195+
196+
$data->setLoginName($loginName);
197+
$data->setServer($server);
198+
199+
// Properly encrypt
200+
$data->setAppPassword($this->encryptPassword($appPassword, $data->getPublicKey()));
201+
202+
$this->mapper->update($data);
203+
return true;
204+
}
205+
189206
public function createTokens(string $userAgent): LoginFlowV2Tokens {
190207
$flow = new LoginFlowV2();
191208
$pollToken = $this->random->generate(128, ISecureRandom::CHAR_DIGITS.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_UPPER);

core/routes.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
['name' => 'ClientFlowLoginV2#grantPage', 'url' => '/login/v2/grant', 'verb' => 'GET'],
6969
['name' => 'ClientFlowLoginV2#generateAppPassword', 'url' => '/login/v2/grant', 'verb' => 'POST'],
7070
['name' => 'ClientFlowLoginV2#init', 'url' => '/login/v2', 'verb' => 'POST'],
71+
['name' => 'ClientFlowLoginV2#apptokenRedirect', 'url' => '/login/v2/apptoken', 'verb' => 'POST'],
7172
['name' => 'TwoFactorChallenge#selectChallenge', 'url' => '/login/selectchallenge', 'verb' => 'GET'],
7273
['name' => 'TwoFactorChallenge#showChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'GET'],
7374
['name' => 'TwoFactorChallenge#solveChallenge', 'url' => '/login/challenge/{challengeProviderId}', 'verb' => 'POST'],

core/templates/loginflowv2/authpicker.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
style('core', 'login/authpicker');
23+
script('core', 'login/authpicker');
2324

2425
/** @var array $_ */
2526
/** @var \OCP\IURLGenerator $urlGenerator */
@@ -50,4 +51,21 @@
5051
</a>
5152
</p>
5253

54+
<form action="<?php p($urlGenerator->linkToRouteAbsolute('core.ClientFlowLoginV2.apptokenRedirect')); ?>" method="post" id="app-token-login-field" class="hidden">
55+
<p class="grouptop">
56+
<input type="text" name="user" id="user" placeholder="<?php p($l->t('Username')) ?>">
57+
<label for="user" class="infield"><?php p($l->t('Username')) ?></label>
58+
</p>
59+
<p class="groupbottom">
60+
<input type="password" name="password" id="password" placeholder="<?php p($l->t('App token')) ?>">
61+
<label for="password" class="infield"><?php p($l->t('Password')) ?></label>
62+
</p>
63+
<input type="hidden" name="stateToken" value="<?php p($_['stateToken']) ?>" />
64+
<input type="hidden" name="requesttoken" value="<?php p($_['requesttoken']) ?>">
65+
<input id="submit-app-token-login" type="submit" class="login primary icon-confirm-white" value="<?php p($l->t('Grant access')) ?>">
66+
</form>
67+
5368
</div>
69+
<?php if (empty($_['oauthState'])): ?>
70+
<a id="app-token-login" class="warning" href="#"><?php p($l->t('Alternative log in using app token')) ?></a>
71+
<?php endif; ?>

0 commit comments

Comments
 (0)