Skip to content

Commit f6298da

Browse files
committed
Merge #9 Telekom bearer token: handling
2 parents 2420a2e + 73344ba commit f6298da

File tree

9 files changed

+1279
-0
lines changed

9 files changed

+1279
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<?php
2+
3+
namespace OCA\UserOIDC\MagentaBearer;
4+
5+
use Exception;
6+
7+
class InvalidTokenException extends Exception {
8+
}

lib/MagentaBearer/MBackend.php

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
/**
5+
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
6+
*
7+
* @author Roeland Jago Douma <roeland@famdouma.nl>
8+
*
9+
* @license GNU AGPL version 3 or any later version
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Affero General Public License as
13+
* published by the Free Software Foundation, either version 3 of the
14+
* License, or (at your option) any later version.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU Affero General Public License for more details.
20+
*
21+
* You should have received a copy of the GNU Affero General Public License
22+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
23+
*
24+
*/
25+
26+
namespace OCA\UserOIDC\MagentaBearer;
27+
28+
use OCA\UserOIDC\User\AbstractOidcBackend;
29+
use OCA\UserOIDC\Db\Provider;
30+
use OCA\UserOIDC\Event\TokenValidatedEvent;
31+
use OCA\UserOIDC\Service\DiscoveryService;
32+
use OCA\UserOIDC\Service\ProviderService;
33+
use OCA\UserOIDC\Service\ProvisioningEventService;
34+
use OCA\UserOIDC\AppInfo\Application;
35+
use OCA\UserOIDC\Db\ProviderMapper;
36+
use OCA\UserOIDC\Db\UserMapper;
37+
use OCP\EventDispatcher\IEventDispatcher;
38+
use OCP\IConfig;
39+
use OCP\IRequest;
40+
use OCP\ISession;
41+
use OCP\IURLGenerator;
42+
use OCP\IUserManager;
43+
use OCP\Security\ICrypto;
44+
use Psr\Log\LoggerInterface;
45+
46+
class MBackend extends AbstractOidcBackend {
47+
48+
/**
49+
* @var TokenService
50+
*/
51+
protected $mtokenService;
52+
53+
/**
54+
* @var ProvisioningEventService
55+
*/
56+
protected $provisioningService;
57+
58+
public function __construct(IConfig $config,
59+
UserMapper $userMapper,
60+
LoggerInterface $logger,
61+
IRequest $request,
62+
ISession $session,
63+
IURLGenerator $urlGenerator,
64+
IEventDispatcher $eventDispatcher,
65+
DiscoveryService $discoveryService,
66+
ProviderMapper $providerMapper,
67+
ProviderService $providerService,
68+
IUserManager $userManager,
69+
ICrypto $crypto,
70+
TokenService $mtokenService,
71+
ProvisioningEventService $provisioningService
72+
) {
73+
parent::__construct($config, $userMapper, $logger, $request, $session,
74+
$urlGenerator, $eventDispatcher, $discoveryService,
75+
$providerMapper, $providerService, $userManager);
76+
77+
$this->mtokenService = $mtokenService;
78+
$this->provisioningService = $provisioningService;
79+
$this->crypto = $crypto;
80+
}
81+
82+
public function getBackendName(): string {
83+
return Application::APP_ID . "\\MagentaBearer";
84+
}
85+
86+
/**
87+
* Backend is activated if header bearer token is detected.
88+
*
89+
* @return bool ture if bearer header found
90+
*/
91+
public function isSessionActive(): bool {
92+
// if this returns true, getCurrentUserId is called
93+
// not sure if we should rather to the validation in here as otherwise it might fail for other backends or bave other side effects
94+
$headerToken = $this->request->getHeader(Application::OIDC_API_REQ_HEADER);
95+
// session is active if we have a bearer token (API request) OR if we logged in via user_oidc (we have a provider ID in the session)
96+
return (preg_match('/^\s*bearer\s+/i', $headerToken) != false);
97+
}
98+
99+
/**
100+
* Return the id of the current user
101+
* @return string
102+
*/
103+
public function getCurrentUserId(): string {
104+
// get the bearer token from headers
105+
$headerToken = $this->request->getHeader(Application::OIDC_API_REQ_HEADER);
106+
$headerToken = preg_replace('/^bearer\s+/i', '', $headerToken);
107+
if ($headerToken === '') {
108+
$this->logger->debug('No Bearer token');
109+
return '';
110+
}
111+
112+
$providers = $this->providerMapper->getProviders();
113+
if (count($providers) === 0) {
114+
$this->logger->debug('no OIDC providers');
115+
return '';
116+
}
117+
118+
// we implement only Telekom behavior (which includes auto-provisioning)
119+
// so we neglect switches from the upstream Nexrcloud oidc handling
120+
121+
// try to validate with all providers
122+
foreach ($providers as $provider) {
123+
if ($this->providerService->getSetting($provider->getId(), ProviderService::SETTING_CHECK_BEARER, '0') === '1') {
124+
try {
125+
$sharedSecret = $this->crypto->decrypt($provider->getBearerSecret());
126+
$bearerToken = $this->mtokenService->decryptToken($headerToken, $sharedSecret);
127+
$this->mtokenService->verifySignature($bearerToken, $sharedSecret);
128+
$payload = $this->mtokenService->decode($bearerToken);
129+
$this->mtokenService->verifyClaims($payload, ['http://auth.magentacloud.de']);
130+
} catch (InvalidTokenException $eToken) {
131+
// there is
132+
$this->logger->debug('Invalid token:' . $eToken->getMessage(). ". Trying another provider.");
133+
continue;
134+
} catch (SignatureException $eSignature) {
135+
// only the key seems not to fit, so try the next provider
136+
$this->logger->debug($eSignature->getMessage() . ". Trying another provider.");
137+
continue;
138+
} catch (\Throwable $e) {
139+
// there is
140+
$this->logger->debug('General non matching provider problem:' . $e->getMessage());
141+
continue;
142+
}
143+
144+
$uidAttribute = $this->providerService->getSetting($provider->getId(), ProviderService::SETTING_MAPPING_UID, 'sub');
145+
$userId = $payload->{$uidAttribute};
146+
if ($userId === null) {
147+
$this->logger->debug('No extractable user id, check mapping!');
148+
return '';
149+
}
150+
151+
// check bearercache here, not skipping validation for security reasons
152+
153+
// Telekom bearer does not support refersh_token, so the pupose of TokenValidatedEvent is not given,
154+
// but could produce trouble if not send with the field, apart from performance aspects.
155+
//
156+
// $discovery = $this->discoveryService->obtainDiscovery($provider);
157+
// $this->eventDispatcher->dispatchTyped(new TokenValidatedEvent(['token' => $payload], $provider, $discovery));
158+
159+
try {
160+
$this->provisioningService->provisionUser($userId, $provider->getId(), $payload);
161+
$this->checkFirstLogin($userId); // create the folders same as on web login
162+
return $userId;
163+
} catch (ProvisioningDeniedException $denied) {
164+
$this->logger->error('Bearer token access denied: ' . $denied->getMessage());
165+
return '';
166+
}
167+
}
168+
}
169+
170+
$this->logger->debug('Could not find provider for token');
171+
return '';
172+
}
173+
174+
/**
175+
* FIXXME: send proper error status from BAckend errors
176+
*
177+
* This function sets an https status code here (early in the failing backend operation)
178+
* to pass on bearer errors cleanly with correct status code and a readable reason
179+
*
180+
* For this, there is a "tricky" setting of a header needed to make it working in all
181+
* known situations, see
182+
* https://stackoverflow.com/questions/3258634/php-how-to-send-http-response-code
183+
*/
184+
// protected function sendHttpStatus(int $httpStatusCode, string $httpStatusMsg) {
185+
// $phpSapiName = substr(php_sapi_name(), 0, 3);
186+
// if ($phpSapiName == 'cgi' || $phpSapiName == 'fpm') {
187+
// header('Status: ' . $httpStatusCode . ' ' . $httpStatusMsg);
188+
// } else {
189+
// $protocol = isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0';
190+
// header($protocol . ' ' . $httpStatusCode . ' ' . $httpStatusMsg);
191+
// }
192+
// }
193+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?php
2+
3+
namespace OCA\UserOIDC\MagentaBearer;
4+
5+
class SignatureException extends InvalidTokenException {
6+
}

0 commit comments

Comments
 (0)