3939use OCP \AppFramework \Http ;
4040use OCP \AppFramework \Http \JSONResponse ;
4141use OCP \AppFramework \Utility \ITimeFactory ;
42+ use OCP \DB \Exception ;
4243use OCP \IRequest ;
4344use OCP \Security \ICrypto ;
4445use OCP \Security \ISecureRandom ;
4546use Psr \Log \LoggerInterface ;
4647
4748class OauthApiController extends Controller {
49+ // the authorization code expires after 10 minutes
50+ public const AUTHORIZATION_CODE_EXPIRES_AFTER = 10 * 60 ;
4851
4952 public function __construct (
5053 string $ appName ,
@@ -54,9 +57,9 @@ public function __construct(
5457 private ClientMapper $ clientMapper ,
5558 private TokenProvider $ tokenProvider ,
5659 private ISecureRandom $ secureRandom ,
57- private ITimeFactory $ time ,
60+ private ITimeFactory $ timeFactory ,
5861 private LoggerInterface $ logger ,
59- private Throttler $ throttler
62+ private Throttler $ throttler,
6063 ) {
6164 parent ::__construct ($ appName , $ request );
6265 }
@@ -67,13 +70,17 @@ public function __construct(
6770 * @BruteForceProtection(action=oauth2GetToken)
6871 *
6972 * @param string $grant_type
70- * @param string $code
71- * @param string $refresh_token
72- * @param string $client_id
73- * @param string $client_secret
73+ * @param string|null $code
74+ * @param string|null $refresh_token
75+ * @param string|null $client_id
76+ * @param string|null $client_secret
7477 * @return JSONResponse
78+ * @throws Exception
7579 */
76- public function getToken ($ grant_type , $ code , $ refresh_token , $ client_id , $ client_secret ): JSONResponse {
80+ public function getToken (
81+ string $ grant_type , ?string $ code , ?string $ refresh_token ,
82+ ?string $ client_id , ?string $ client_secret
83+ ): JSONResponse {
7784
7885 // We only handle two types
7986 if ($ grant_type !== 'authorization_code ' && $ grant_type !== 'refresh_token ' ) {
@@ -99,6 +106,33 @@ public function getToken($grant_type, $code, $refresh_token, $client_id, $client
99106 return $ response ;
100107 }
101108
109+ if ($ grant_type === 'authorization_code ' ) {
110+ // check this token is in authorization code state
111+ $ deliveredTokenCount = $ accessToken ->getTokenCount ();
112+ if ($ deliveredTokenCount > 0 ) {
113+ $ response = new JSONResponse ([
114+ 'error ' => 'invalid_request ' ,
115+ ], Http::STATUS_BAD_REQUEST );
116+ $ response ->throttle (['invalid_request ' => 'authorization_code_received_for_active_token ' ]);
117+ return $ response ;
118+ }
119+
120+ // check authorization code expiration
121+ $ now = $ this ->timeFactory ->now ()->getTimestamp ();
122+ $ codeCreatedAt = $ accessToken ->getCodeCreatedAt ();
123+ if ($ codeCreatedAt < $ now - self ::AUTHORIZATION_CODE_EXPIRES_AFTER ) {
124+ // we know this token is not useful anymore
125+ $ this ->accessTokenMapper ->delete ($ accessToken );
126+
127+ $ response = new JSONResponse ([
128+ 'error ' => 'invalid_request ' ,
129+ ], Http::STATUS_BAD_REQUEST );
130+ $ expiredSince = $ now - self ::AUTHORIZATION_CODE_EXPIRES_AFTER - $ codeCreatedAt ;
131+ $ response ->throttle (['invalid_request ' => 'authorization_code_expired ' , 'expired_since ' => $ expiredSince ]);
132+ return $ response ;
133+ }
134+ }
135+
102136 try {
103137 $ client = $ this ->clientMapper ->getByUid ($ accessToken ->getClientId ());
104138 } catch (ClientNotFoundException $ e ) {
@@ -159,13 +193,18 @@ public function getToken($grant_type, $code, $refresh_token, $client_id, $client
159193 );
160194
161195 // Expiration is in 1 hour again
162- $ appToken ->setExpires ($ this ->time ->getTime () + 3600 );
196+ $ appToken ->setExpires ($ this ->timeFactory ->getTime () + 3600 );
163197 $ this ->tokenProvider ->updateToken ($ appToken );
164198
165199 // Generate a new refresh token and encrypt the new apptoken in the DB
166200 $ newCode = $ this ->secureRandom ->generate (128 , ISecureRandom::CHAR_ALPHANUMERIC );
167201 $ accessToken ->setHashedCode (hash ('sha512 ' , $ newCode ));
168202 $ accessToken ->setEncryptedToken ($ this ->crypto ->encrypt ($ newToken , $ newCode ));
203+ // increase the number of delivered oauth token
204+ // this helps with cleaning up DB access token when authorization code has expired
205+ // and it never delivered any oauth token
206+ $ tokenCount = $ accessToken ->getTokenCount ();
207+ $ accessToken ->setTokenCount ($ tokenCount + 1 );
169208 $ this ->accessTokenMapper ->update ($ accessToken );
170209
171210 $ this ->throttler ->resetDelay ($ this ->request ->getRemoteAddress (), 'login ' , ['user ' => $ appToken ->getUID ()]);
0 commit comments