public function run( $credentials ) { $status = self::STATUS_TOKEN_INVALID; // Assume invalid token as a default, safer if ( !isset( $credentials->id ) ) { return self::STATUS_TOKEN_UNAVAILABLE; } else { // Fetch and validate token for validity and optionally scope. // Either let the request pass, or immediately bail with 401. // Section 5.2.1 for error handling. // // invalid_request missing required params -> 400 // // invalid_token Expired token which cannot be refreshed -> 401 // // expired_token Token has expired -> 401 // // insufficient_scope The requested scope is outside scope associated with token -> 403 // // Do not include error info for requests which did not contain auth details.ref. 5.2.1 // Checking for existance of token // Query made below will check if user's access grant is still valid $tokenInfo = ezpRestToken::fetch( $credentials->id ); if ( !$tokenInfo instanceof ezpRestToken ) { return self::STATUS_TOKEN_INVALID; //throw new ezpOauthInvalidTokenException( "Specified token does not exist." ); } // Check expiry of token if ( $tokenInfo->expirytime > 0 ) { if ( $tokenInfo->expirytime < time() ) { $d = date( "c", $tokenInfo->expirytime ); return self::STATUS_TOKEN_EXPIRED; //throw new ezpOauthExpiredTokenException( "Token expired on {$d}" ); } } self::$tokenInfo = $tokenInfo; // Scope checking to be implemented. // Currently some hooks ought to be added to eZP to maximise the // benefit to this field. $status = self::STATUS_OK; } return $status; }
/** * Generates a new token against an authorization_code * Auth code is checked against clientId, clientSecret and redirectUri as registered for client in admin * Auth code is for one-use only and will be removed once the access token generated * @param string $clientId Client identifier * @param string $clientSecret Client secret key * @param string $authCode Authorization code provided by the client * @param string $redirectUri Redirect URI. Must be the same as registered in admin * @return ezpRestToken * @throws ezpOauthInvalidRequestException * @throws ezpOauthInvalidTokenException * @throws ezpOauthExpiredTokenException */ public static function doRefreshTokenWithAuthorizationCode($clientId, $clientSecret, $authCode, $redirectUri) { $client = ezpRestClient::fetchByClientId($clientId); $tokenTTL = (int) eZINI::instance('rest.ini')->variable('OAuthSettings', 'TokenTTL'); if (!$client instanceof ezpRestClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->validateSecret($clientSecret)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->isEndPointValid($redirectUri)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_REQUEST); } $session = ezcPersistentSessionInstance::get(); $q = $session->createFindQuery('ezpRestAuthcode'); $q->where($q->expr->eq('id', $q->bindValue($authCode))); $codeInfo = $session->find($q, 'ezpRestAuthcode'); if (empty($codeInfo)) { throw new ezpOauthInvalidTokenException("Specified authorization code does not exist."); } $codeInfo = array_shift($codeInfo); // Validate client is still authorized, then validate code is not expired $authorized = ezpRestAuthorizedClient::fetchForClientUser($client, eZUser::fetch($codeInfo->user_id)); if (!$authorized instanceof ezpRestAuthorizedClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } // Check expiry of authorization_code if ($codeInfo->expirytime != 0) { if ($codeInfo->expirytime < time()) { $d = date("c", $codeInfo->expirytime); throw new ezpOauthExpiredTokenException("Authorization code expired on {$d}"); } } // code ok, create access and refresh tokens $accessToken = ezpRestToken::generateToken(''); $refreshToken = ezpRestToken::generateToken(''); $token = new ezpRestToken(); $token->id = $accessToken; $token->refresh_token = $refreshToken; $token->client_id = $clientId; $token->user_id = $codeInfo->user_id; $token->expirytime = time() + $tokenTTL; $session = ezcPersistentSessionInstance::get(); $session->save($token); // After an auth code is used, we'll remove it so it is not abused $session->delete($codeInfo); return $token; }
// Redirect to the redirect_uri with these parameters: // - code, only if request_type == code OR code_and_token @todo Implement // - access_token, only if request_type == token OR code_and_token // - expires_in, the token lifetime in seconds // - scope, the permission scope the provided code / token grants, if different from the requested one (not implemented yet) // - state, not implemented yet (state persistency related) $parameters = array(); $rExpiresIn = $tokenTTL; $parameters[] = 'access_token=' . urlencode($rAccessToken); $parameters[] = 'refresh_token=' . urlencode($rRefreshToken); $parameters[] = "expires_in={$rExpiresIn}"; $location = "{$pRedirectUri}?" . implode($parameters, '&'); response('302 Found', $location); } elseif ($pResponseType == 'code') { // At this point, the we know the user HAS granted access, and can hand over a token $rCode = ezpRestToken::generateToken($pScope); $code = new ezpRestAuthcode(); $code->id = $rCode; $code->client_id = $pClientId; $code->user_id = $user->attribute('contentobject_id'); $code->expirytime = time() + $tokenTTL; $session = ezcPersistentSessionInstance::get(); $session->save($code); // The user has a agreed to authorize this app. // Redirect to the redirect_uri with these parameters: // - code, only if request_type == code OR code_and_token @todo Implement // - access_token, only if request_type == token OR code_and_token // - expires_in, the token lifetime in seconds // - scope, the permission scope the provided code / token grants, if different from the requested one (not implemented yet) // - state, not implemented yet (state persistency related) $parameters = array();
/** * Handles the POST request which is sent to obtain token data. * * Currently only authorization code access grant is supported, section 4.1.1 * * @return ezcMvcResult */ public function doHandleRequest() { // Check that the correct params are present // Params for token endpoint per section 4 // REQUIRED: grant_type, client_id, client_secret // OPTIONAL: scope // // Supported grant types are: authorization_code and refresh_token // // Additional params for 'authorization_code', Section 4.1.1 // REQUIRED: code, redirect_uri // // Additional param for 'refresh_token", Section 4.1.4 // REQUIRED: refresh_token // Defining the required params for each stage of operation $initialRequiredParams = array('grant_type', 'client_id', 'client_secret'); // params for grant_type=authorization_code $codeRequiredParams = array('code', 'redirect_uri'); // params for grant_type=refresh_token $refreshRequiredParams = array('refresh_token'); $this->checkParams($initialRequiredParams); // We can get the first set of required params $grant_type = $this->request->post['grant_type']; $client_id = $this->request->post['client_id']; $client_secret = $this->request->post['client_secret']; if (!$this->validateGrantType($grant_type)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::UNSUPPORTED_GRANT_TYPE); } switch ($grant_type) { case 'authorization_code': $this->checkParams($codeRequiredParams); $authCode = $this->request->post['code']; $redirect_uri = $this->request->post['redirect_uri']; $client = ezpRestClient::fetchByClientId($client_id); if (!$client instanceof ezpRestClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->validateSecret($client_secret)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } if (!$client->isEndPointValid($redirect_uri)) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_REQUEST); } $session = ezcPersistentSessionInstance::get(); $q = $session->createFindQuery('ezpRestAuthcode'); $q->where($q->expr->eq('id', $q->bindValue($authCode))); $codeInfo = $session->find($q, 'ezpRestAuthcode'); if (empty($codeInfo)) { // return self::STATUS_TOKEN_INVALID; throw new ezpOauthInvalidTokenException("Specified authorization code does not exist."); } $codeInfo = array_shift($codeInfo); // Validate client is still authorized, then validate code is not expired $authorized = ezpRestAuthorizedClient::fetchForClientUser($client, eZUser::fetch($codeInfo->user_id)); if (!$authorized instanceof ezpRestAuthorizedClient) { throw new ezpOauthInvalidRequestException(ezpOauthTokenEndpointErrorType::INVALID_CLIENT); } // Check expiry of token if ($codeInfo->expirytime !== 0) { if ($codeInfo->expirytime < time()) { $d = date("c", $codeInfo->expirytime); // return self::STATUS_TOKEN_EXPIRED; throw new ezpOauthExpiredTokenException("Token expired on {$d}"); } } // code eok, create access token $accessToken = ezpRestToken::generateToken(''); $refreshToken = ezpRestToken::generateToken(''); $token = new ezpRestToken(); $token->id = $accessToken; $token->refresh_token = $refreshToken; $token->client_id = $client_id; $token->user_id = $codeInfo->user_id; $token->expirytime = time() + 3600; $session = ezcPersistentSessionInstance::get(); $session->save($token); //@TODO for later, check for transmission over https $r = new ezcMvcResult(); $r->status = new ezcMvcExternalRedirect($redirect_uri . '?access_token=' . $token->id); return $r; break; case 'refresh_token': $this->checkParams($refreshRequiredParams); $refreshToken = $this->request->post['refresh_token']; break; } }