mirror of
https://github.com/nextcloud/server.git
synced 2026-05-28 04:32:30 -04:00
Merge pull request #38899 from nextcloud/backport/38773/stable25
[stable25] Add bruteforce protection in OauthApiController
This commit is contained in:
commit
5738ce5cd7
2 changed files with 23 additions and 5 deletions
|
|
@ -86,6 +86,7 @@ class OauthApiController extends Controller {
|
|||
/**
|
||||
* @PublicPage
|
||||
* @NoCSRFRequired
|
||||
* @BruteForceProtection(action=oauth2GetToken)
|
||||
*
|
||||
* @param string $grant_type
|
||||
* @param string $code
|
||||
|
|
@ -98,9 +99,11 @@ class OauthApiController extends Controller {
|
|||
|
||||
// We only handle two types
|
||||
if ($grant_type !== 'authorization_code' && $grant_type !== 'refresh_token') {
|
||||
return new JSONResponse([
|
||||
$response = new JSONResponse([
|
||||
'error' => 'invalid_grant',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$response->throttle(['invalid_grant' => $grant_type]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
// We handle the initial and refresh tokens the same way
|
||||
|
|
@ -111,17 +114,21 @@ class OauthApiController extends Controller {
|
|||
try {
|
||||
$accessToken = $this->accessTokenMapper->getByCode($code);
|
||||
} catch (AccessTokenNotFoundException $e) {
|
||||
return new JSONResponse([
|
||||
$response = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$response->throttle(['invalid_request' => 'token not found', 'code' => $code]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
try {
|
||||
$client = $this->clientMapper->getByUid($accessToken->getClientId());
|
||||
} catch (ClientNotFoundException $e) {
|
||||
return new JSONResponse([
|
||||
$response = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$response->throttle(['invalid_request' => 'client not found', 'client_id' => $accessToken->getClientId()]);
|
||||
return $response;
|
||||
}
|
||||
|
||||
if (isset($this->request->server['PHP_AUTH_USER'])) {
|
||||
|
|
@ -133,15 +140,18 @@ class OauthApiController extends Controller {
|
|||
$storedClientSecret = $this->crypto->decrypt($client->getSecret());
|
||||
} catch (\Exception $e) {
|
||||
$this->logger->error('OAuth client secret decryption error', ['exception' => $e]);
|
||||
// we don't throttle here because it might not be a bruteforce attack
|
||||
return new JSONResponse([
|
||||
'error' => 'invalid_client',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
}
|
||||
// The client id and secret must match. Else we don't provide an access token!
|
||||
if ($client->getClientIdentifier() !== $client_id || $storedClientSecret !== $client_secret) {
|
||||
return new JSONResponse([
|
||||
$response = new JSONResponse([
|
||||
'error' => 'invalid_client',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$response->throttle(['invalid_client' => 'client ID or secret does not match']);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$decryptedToken = $this->crypto->decrypt($accessToken->getEncryptedToken(), $code);
|
||||
|
|
@ -154,9 +164,11 @@ class OauthApiController extends Controller {
|
|||
} catch (InvalidTokenException $e) {
|
||||
//We can't do anything...
|
||||
$this->accessTokenMapper->delete($accessToken);
|
||||
return new JSONResponse([
|
||||
$response = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$response->throttle(['invalid_request' => 'token is invalid']);
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Rotate the apptoken (so the old one becomes invalid basically)
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_grant',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_grant' => 'foo']);
|
||||
|
||||
$this->assertEquals($expected, $this->oauthApiController->getToken('foo', null, null, null, null));
|
||||
}
|
||||
|
|
@ -107,6 +108,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_request' => 'token not found', 'code' => 'invalidcode']);
|
||||
|
||||
$this->accessTokenMapper->method('getByCode')
|
||||
->with('invalidcode')
|
||||
|
|
@ -119,6 +121,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_request' => 'token not found', 'code' => 'invalidrefresh']);
|
||||
|
||||
$this->accessTokenMapper->method('getByCode')
|
||||
->with('invalidrefresh')
|
||||
|
|
@ -131,6 +134,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_request' => 'client not found', 'client_id' => 42]);
|
||||
|
||||
$accessToken = new AccessToken();
|
||||
$accessToken->setClientId(42);
|
||||
|
|
@ -164,6 +168,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_client',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_client' => 'client ID or secret does not match']);
|
||||
|
||||
$accessToken = new AccessToken();
|
||||
$accessToken->setClientId(42);
|
||||
|
|
@ -186,6 +191,7 @@ class OauthApiControllerTest extends TestCase {
|
|||
$expected = new JSONResponse([
|
||||
'error' => 'invalid_request',
|
||||
], Http::STATUS_BAD_REQUEST);
|
||||
$expected->throttle(['invalid_request' => 'token is invalid']);
|
||||
|
||||
$accessToken = new AccessToken();
|
||||
$accessToken->setClientId(42);
|
||||
|
|
|
|||
Loading…
Reference in a new issue