|
46 | 46 | use OC\Authentication\Exceptions\PasswordLoginForbiddenException; |
47 | 47 | use OC\Authentication\Token\IProvider; |
48 | 48 | use OC\Authentication\Token\IToken; |
| 49 | +use OC\Authentication\Token\PublicKeyToken; |
49 | 50 | use OC\Hooks\Emitter; |
50 | 51 | use OC\Hooks\PublicEmitter; |
51 | 52 | use OC_User; |
@@ -445,7 +446,14 @@ public function logClientIn($user, |
445 | 446 | } |
446 | 447 |
|
447 | 448 | try { |
448 | | - $isTokenPassword = $this->isTokenPassword($password); |
| 449 | + $dbToken = $this->getTokenFromPassword($password); |
| 450 | + $isTokenPassword = $dbToken !== null; |
| 451 | + if (($dbToken instanceof PublicKeyToken) |
| 452 | + && ($dbToken->getType() !== IToken::PERMANENT_TOKEN) |
| 453 | + ) { |
| 454 | + // Refuse session tokens here, only app tokens are handled |
| 455 | + return false; |
| 456 | + } |
449 | 457 | } catch (ExpiredTokenException $e) { |
450 | 458 | // Just return on an expired token no need to check further or record a failed login |
451 | 459 | return false; |
@@ -542,6 +550,24 @@ public function isTokenPassword($password) { |
542 | 550 | } |
543 | 551 | } |
544 | 552 |
|
| 553 | + /** |
| 554 | + * Check if the given 'password' is actually a device token |
| 555 | + * |
| 556 | + * @throws ExpiredTokenException |
| 557 | + */ |
| 558 | + private function getTokenFromPassword(string $password): ?IToken { |
| 559 | + try { |
| 560 | + return $this->tokenProvider->getToken($password); |
| 561 | + } catch (ExpiredTokenException $e) { |
| 562 | + throw $e; |
| 563 | + } catch (InvalidTokenException $ex) { |
| 564 | + $this->logger->debug('Token is not valid: ' . $ex->getMessage(), [ |
| 565 | + 'exception' => $ex, |
| 566 | + ]); |
| 567 | + return null; |
| 568 | + } |
| 569 | + } |
| 570 | + |
545 | 571 | protected function prepareUserLogin($firstTimeLogin, $refreshCsrfToken = true) { |
546 | 572 | if ($refreshCsrfToken) { |
547 | 573 | // TODO: mock/inject/use non-static |
@@ -819,29 +845,36 @@ private function validateToken($token, $user = null) { |
819 | 845 | */ |
820 | 846 | public function tryTokenLogin(IRequest $request) { |
821 | 847 | $authHeader = $request->getHeader('Authorization'); |
| 848 | + $tokenFromCookie = false; |
822 | 849 | if (strpos($authHeader, 'Bearer ') === 0) { |
823 | 850 | $token = substr($authHeader, 7); |
824 | 851 | } else { |
825 | 852 | // No auth header, let's try session id |
826 | 853 | try { |
827 | 854 | $token = $this->session->getId(); |
| 855 | + $tokenFromCookie = true; |
828 | 856 | } catch (SessionNotAvailableException $ex) { |
829 | 857 | return false; |
830 | 858 | } |
831 | 859 | } |
832 | 860 |
|
833 | | - if (!$this->loginWithToken($token)) { |
| 861 | + try { |
| 862 | + $dbToken = $this->tokenProvider->getToken($token); |
| 863 | + } catch (InvalidTokenException $e) { |
| 864 | + // Can't really happen but better safe than sorry |
834 | 865 | return false; |
835 | 866 | } |
836 | | - if (!$this->validateToken($token)) { |
| 867 | + |
| 868 | + if ($dbToken instanceof PublicKeyToken && $dbToken->getType() === IToken::TEMPORARY_TOKEN && !$tokenFromCookie) { |
| 869 | + // Session token but from Bearer header, not allowed |
837 | 870 | return false; |
838 | 871 | } |
839 | 872 |
|
840 | | - try { |
841 | | - $dbToken = $this->tokenProvider->getToken($token); |
842 | | - } catch (InvalidTokenException $e) { |
843 | | - // Can't really happen but better save than sorry |
844 | | - return true; |
| 873 | + if (!$this->loginWithToken($token)) { |
| 874 | + return false; |
| 875 | + } |
| 876 | + if (!$this->validateToken($token)) { |
| 877 | + return false; |
845 | 878 | } |
846 | 879 |
|
847 | 880 | // Remember me tokens are not app_passwords |
|
0 commit comments