123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- <?php
- /**
- * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch>
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- namespace OCA\OAuth2\Controller;
- use OC\Authentication\Exceptions\InvalidTokenException;
- use OC\Authentication\Token\ExpiredTokenException;
- use OC\Authentication\Token\IProvider as TokenProvider;
- use OCA\OAuth2\Db\AccessTokenMapper;
- use OCA\OAuth2\Db\ClientMapper;
- use OCA\OAuth2\Exceptions\AccessTokenNotFoundException;
- use OCA\OAuth2\Exceptions\ClientNotFoundException;
- use OCP\AppFramework\Controller;
- use OCP\AppFramework\Http;
- use OCP\AppFramework\Http\JSONResponse;
- use OCP\AppFramework\Utility\ITimeFactory;
- use OCP\IRequest;
- use OCP\Security\ICrypto;
- use OCP\Security\ISecureRandom;
- class OauthApiController extends Controller {
- /** @var AccessTokenMapper */
- private $accessTokenMapper;
- /** @var ClientMapper */
- private $clientMapper;
- /** @var ICrypto */
- private $crypto;
- /** @var TokenProvider */
- private $tokenProvider;
- /** @var ISecureRandom */
- private $secureRandom;
- /** @var ITimeFactory */
- private $time;
- /**
- * @param string $appName
- * @param IRequest $request
- * @param ICrypto $crypto
- * @param AccessTokenMapper $accessTokenMapper
- * @param ClientMapper $clientMapper
- * @param TokenProvider $tokenProvider
- * @param ISecureRandom $secureRandom
- * @param ITimeFactory $time
- */
- public function __construct($appName,
- IRequest $request,
- ICrypto $crypto,
- AccessTokenMapper $accessTokenMapper,
- ClientMapper $clientMapper,
- TokenProvider $tokenProvider,
- ISecureRandom $secureRandom,
- ITimeFactory $time) {
- parent::__construct($appName, $request);
- $this->crypto = $crypto;
- $this->accessTokenMapper = $accessTokenMapper;
- $this->clientMapper = $clientMapper;
- $this->tokenProvider = $tokenProvider;
- $this->secureRandom = $secureRandom;
- $this->time = $time;
- }
- /**
- * @PublicPage
- * @NoCSRFRequired
- *
- * @param string $grant_type
- * @param string $code
- * @param string $refresh_token
- * @param string $client_id
- * @param string $client_secret
- * @return JSONResponse
- */
- public function getToken($grant_type, $code, $refresh_token, $client_id, $client_secret) {
- // We only handle two types
- if ($grant_type !== 'authorization_code' && $grant_type !== 'refresh_token') {
- return new JSONResponse([
- 'error' => 'invalid_grant',
- ], Http::STATUS_BAD_REQUEST);
- }
- // We handle the initial and refresh tokens the same way
- if ($grant_type === 'refresh_token' ) {
- $code = $refresh_token;
- }
- try {
- $accessToken = $this->accessTokenMapper->getByCode($code);
- } catch (AccessTokenNotFoundException $e) {
- return new JSONResponse([
- 'error' => 'invalid_request',
- ], Http::STATUS_BAD_REQUEST);
- }
- try {
- $client = $this->clientMapper->getByUid($accessToken->getClientId());
- } catch (ClientNotFoundException $e) {
- return new JSONResponse([
- 'error' => 'invalid_request',
- ], Http::STATUS_BAD_REQUEST);
- }
- // The client id and secret must match. Else we don't provide an access token!
- if ($client->getClientIdentifier() !== $client_id || $client->getSecret() !== $client_secret) {
- return new JSONResponse([
- 'error' => 'invalid_client',
- ], Http::STATUS_BAD_REQUEST);
- }
- $decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code);
- // Obtain the appToken assoicated
- try {
- $appToken = $this->tokenProvider->getTokenById($accessToken->getTokenId());
- } catch (ExpiredTokenException $e) {
- $appToken = $e->getToken();
- } catch (InvalidTokenException $e) {
- //We can't do anything...
- $this->accessTokenMapper->delete($accessToken);
- return new JSONResponse([
- 'error' => 'invalid_request',
- ], Http::STATUS_BAD_REQUEST);
- }
- // Rotate the apptoken (so the old one becomes invalid basically)
- $newToken = $this->secureRandom->generate(72, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
- $appToken = $this->tokenProvider->rotate(
- $appToken,
- $decryptedToken,
- $newToken
- );
- // Expiration is in 1 hour again
- $appToken->setExpires($this->time->getTime() + 3600);
- $this->tokenProvider->updateToken($appToken);
- // Generate a new refresh token and encrypt the new apptoken in the DB
- $newCode = $this->secureRandom->generate(128, ISecureRandom::CHAR_UPPER.ISecureRandom::CHAR_LOWER.ISecureRandom::CHAR_DIGITS);
- $accessToken->setHashedCode(hash('sha512', $newCode));
- $accessToken->setEncryptedToken($this->crypto->encrypt($newToken, $newCode));
- $this->accessTokenMapper->update($accessToken);
- return new JSONResponse(
- [
- 'access_token' => $newToken,
- 'token_type' => 'Bearer',
- 'expires_in' => 3600,
- 'refresh_token' => $newCode,
- 'user_id' => $appToken->getUID(),
- ]
- );
- }
- }
|