├── README.markdown └── oauth.php /README.markdown: -------------------------------------------------------------------------------- 1 | PREREQUISITES 2 | ============== 3 | - Session Support in your PHP 4 | - cURL Support in your PHP 5 | - JSON SUpport in your PHP 6 | - SSL Support in your Web Server 7 | 8 | HOW TO INSTALL 9 | ============== 10 | Step 1. Include oauth.php in php script which needs authentication. 11 | ------------------------------------------------------------------- 12 | require_once 'oauth.php'; 13 | 14 | Step 2. Create a new instance of oauth class. 15 | --------------------------------------------- 16 | $oauth = new oauth([CLIENT_ID], [CLIENT_SECRET], [CALLBACK_URL], [LOGIN_URL], [CACHE_DIR]); 17 | 18 | - LOGIN_URL and CACHE_DIR are optional. 19 | - access token, refresh token and instance url are saved under CACHE_DIR in case of using auth_with_password() method. 20 | 21 | Step 3. Do OAuth. 22 | ------------------------------------------------------------------------------------------------------ 23 | $oauth->auth_with_code([LIFETIME]); 24 | If you apply standard web server authetication flow, you can use auth_with_code() method. This type of flow is appropriate when you provide external web services that need access to Force.com/Database.com. LIFETIME is minutes to refresh access token. You can omit LIFETIME and then it is set to 60 by default. 25 | 26 | $oauth->auth_with_password([USERNAME], [PASSWORD]); 27 | If you apply username/password authetication flow, use auth_with_password() method. This type of flow is used in case you need users access to web contents without authentication while the contents still need login to Force.com/Database.com. 28 | 29 | Step 4. Create a directory for CACHE_DIR 30 | ---------------------------------------------------------------- 31 | If you apply username/password authentication flow, Token and Instance URL info will be saved in individual file under the directory which is configured as CACHE_DIR after initial authentication succeeds. Default location of CACHE_DIR is current directory. UNIX web user must has write permission on this directory or you can set another directory by passing 5th argument in creating $oauth instance as follows. 32 | 33 | define('CLIENT_ID', '3MVG9rFJvQRVOvk40dRq5u_ZA0eT2KvZCvZq.XeA1hFtgc3PITGlLMp3V_kKIwtc6IaEGWkIO3cOu0IgVmujh'); 34 | define('CLIENT_SECRET', '1136279981407985294'); 35 | define('CALLBACK_URL', 'https://sugoisurvey.nkjmkzk.net'); 36 | define('LOGIN_URL', 'https://login.salesforce.com'); 37 | define('CACHE_DIR', 'oauth/cache'); 38 | $oauth = new oauth(CLIENT_ID, CLIENT_SECRET, CALLBACK_URL, LOGIN_URL, CACHE_DIR); 39 | 40 | Cache files are refreshed every 60 minutes by default. You can set other value by passing 3rd argument in executing auth_with_password() as follows. 41 | 42 | define('USERNAME', 'nkjm.kzk@gmail.com'); 43 | define('PASSWORD', 'mypassword'); 44 | $oauth->auth_with_password(USERNAME, PASSWORD, 120); 45 | 46 | Step 5. Use Token to access REST Resources. 47 | ----------------------------------------------- 48 | After auth_with_code() or auth_with_password() successfully executed, $oauth instance has following properties set. 49 | You can use these values to access to REST Resources. 50 | 51 | - $oauth->access_token 52 | - $oauth->refresh_token 53 | - $oauth->instance_url 54 | 55 | Sample Code 56 | =========== 57 | Following is the sample code which describe all the required code to provide web server authentication flow. 58 | 59 | auth_with_code(); 69 | 70 | $query = "select name from session__c"; 71 | $url = $oauth->instance_url . "/services/data/v24.0/query?q=" . urlencode($query); 72 | $curl = curl_init($url); 73 | curl_setopt($curl, CURLOPT_HEADER, false); 74 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 75 | curl_setopt($curl, CURLOPT_HTTPHEADER, array("Authorization: OAuth " . $oauth->access_token)); 76 | curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1); 77 | $response = json_decode(curl_exec($curl), true); 78 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); 79 | if ( $status != 200 ) { 80 | die("

Curl Error

URL : " . $url . "

Status : " . $status . "

response : error = " . $response['error'] . ", error_description = " . $response['error_description'] . "

curl_error : " . curl_error($curl) . "

curl_errno : " . curl_errno($curl) . "

"); 81 | } 82 | curl_close($curl); 83 | return($response); 84 | ?> 85 | 86 | Token Refresh 87 | ============= 88 | While this toolkit automatically refreshes access token depending of LIFETIME value, you can invoke token refresh manually by executing auth_with_refresh_token() method as follows. 89 | 90 | $oauth->auth_with_refresh_token(); 91 | 92 | 93 | Learn about OAuth 2.0 on Force.com 94 | ================================== 95 | Following blog article should be a great reference to understand each type of authentication flow in OAuth 2.0. 96 | 97 | [Digging Deeper into OAuth 2.0 on Force.com](http://wiki.developerforce.com/page/Digging_Deeper_into_OAuth_2.0_on_Force.com) 98 | -------------------------------------------------------------------------------- /oauth.php: -------------------------------------------------------------------------------- 1 | 5 | */ 6 | 7 | class oauth { 8 | public $client_id; 9 | public $client_secret; 10 | public $login_url; 11 | public $token_url; 12 | public $callback_url; 13 | public $access_token; 14 | public $refresh_token; 15 | public $instance_url; 16 | public $cache_dir; 17 | public $error = FALSE; 18 | public $error_msg = array(); 19 | 20 | public function __construct($client_id, $client_secret, $callback_url, $login_url = 'https://login.salesforce.com', $cache_dir = '.'){ 21 | $this->client_id = $client_id; 22 | $this->client_secret = $client_secret; 23 | $this->callback_url = $callback_url; 24 | $this->login_url = $login_url; 25 | $this->token_url = $login_url . "/services/oauth2/token"; 26 | $this->cache_dir = $cache_dir; 27 | } 28 | 29 | public function auth_with_code($lifetime = 60){ 30 | session_start(); 31 | $this->read_cache_from_session(); 32 | $this->refresh_cache_on_session($lifetime); 33 | if ($this->error){ 34 | return(FALSE); 35 | } 36 | if (empty($this->access_token) || empty($this->instance_url) || empty($this->refresh_token)){ 37 | //get access code 38 | if (!isset($_GET['code'])){ 39 | $this->redirect_to_get_access_code(); 40 | } 41 | 42 | //set user display depending on user agent 43 | if ($this->get_user_agent() == 'iPhone' || $this->get_user_agent() == 'iPad'){ 44 | $display = 'touch'; 45 | } else { 46 | $display = 'page'; 47 | } 48 | 49 | //get access token 50 | $fragment = "grant_type=authorization_code" 51 | . "&code=" . $_GET['code'] 52 | . "&display=" . $display 53 | . "&client_id=" . $this->client_id 54 | . "&client_secret=" . $this->client_secret 55 | . "&redirect_uri=" . urlencode($this->callback_url); 56 | $response = $this->send($fragment); 57 | if ($this->error){ 58 | if (array_pop($this->error_msg) == 'new code required'){ 59 | $this->redirect_to_get_access_code(); 60 | } else { 61 | return(FALSE); 62 | } 63 | } 64 | $this->access_token = $response['access_token']; 65 | $this->refresh_token = $response['refresh_token']; 66 | $this->instance_url = $response['instance_url']; 67 | $this->save_to_session(); 68 | } 69 | return(TRUE); 70 | } 71 | 72 | public function auth_with_password($username, $password, $lifetime = 60){ 73 | $this->refresh_cache_on_filesystem($lifetime); 74 | if ($this->error){ 75 | return(FALSE); 76 | } 77 | $this->read_cache_from_filesystem(); 78 | if ($this->error){ 79 | return(FALSE); 80 | } 81 | if (empty($this->access_token) || empty($this->instance_url)){ 82 | $fragment = "grant_type=password" 83 | . "&client_id=" . $this->client_id 84 | . "&client_secret=" . $this->client_secret 85 | . "&username=" . $username 86 | . "&password=" . urlencode($password); 87 | $response = $this->send($fragment); 88 | if ($this->error){ 89 | return(FALSE); 90 | } 91 | $this->access_token = $response['access_token']; 92 | $this->refresh_token = ''; 93 | $this->instance_url = $response['instance_url']; 94 | $this->save_to_filesystem(); 95 | if ($this->error){ 96 | return(FALSE); 97 | } 98 | } 99 | return(TRUE); 100 | } 101 | 102 | public function auth_with_refresh_token(){ 103 | $fragment = "grant_type=refresh_token" 104 | . "&client_id=" . $this->client_id 105 | . "&client_secret=" . $this->client_secret 106 | . "&refresh_token=" . $this->refresh_token; 107 | $response = $this->send($fragment); 108 | if ($this->error){ 109 | return(FALSE); 110 | } 111 | $this->access_token = $response['access_token']; 112 | $this->save_to_session(); 113 | return(TRUE); 114 | } 115 | 116 | private function get_user_agent(){ 117 | $f = explode(';', $_SERVER['HTTP_USER_AGENT']); 118 | $ff = explode('(', $f[0]); 119 | return(trim($ff[1])); 120 | } 121 | 122 | private function redirect_to_get_access_code(){ 123 | $auth_url = $this->login_url . "/services/oauth2/authorize?response_type=code&client_id=" . $this->client_id . "&redirect_uri=" . urlencode($this->callback_url); 124 | header('Location: ' . $auth_url); 125 | } 126 | 127 | private function redirect_to_get_access_token(){ 128 | $auth_url = $this->login_url . "/services/oauth2/authorize?response_type=token&client_id=" . $this->client_id . "&redirect_uri=" . urlencode($this->callback_url); 129 | header('Location: ' . $auth_url); 130 | } 131 | 132 | private function refresh_cache_on_session($lifetime){ 133 | if (isset($_SESSION['created_at'])){ 134 | $current_time = time(); 135 | if (($current_time - $_SESSION['created_at']) > $lifetime * 60){ 136 | $this->auth_with_refresh_token(); 137 | } 138 | } 139 | } 140 | 141 | private function refresh_cache_on_filesystem($lifetime){ 142 | if (is_file($this->cache_dir . "/access_token")){ 143 | $current_time = time(); 144 | $mtime = filemtime($this->cache_dir . "/access_token"); 145 | if (($current_time - $mtime) > $lifetime * 60){ 146 | if (!unlink($this->cache_dir . "/access_token")){ 147 | $this->set_error("Failed to unlink " . $this->cache_dir . "/access_token."); 148 | return; 149 | } 150 | } 151 | } 152 | } 153 | 154 | private function read_cache_from_filesystem(){ 155 | $array_cache = array("access_token", "refresh_token", "instance_url"); 156 | foreach($array_cache as $k => $v){ 157 | if (is_file($this->cache_dir . "/" . $v)){ 158 | $fp = fopen($this->cache_dir . "/" . $v, "r"); 159 | if ($fp == FALSE){ 160 | $this->set_error("Failed to open " . $this->cache_dir . "/" . $v . " in read mode."); 161 | return; 162 | } 163 | $this->$v = fgets($fp); 164 | fclose($fp); 165 | } 166 | } 167 | } 168 | 169 | private function read_cache_from_session(){ 170 | $array_cache = array("access_token", "refresh_token", "instance_url"); 171 | foreach($array_cache as $k => $v){ 172 | if (isset($_SESSION[$v])){ 173 | $this->$v = $_SESSION[$v]; 174 | } 175 | } 176 | } 177 | 178 | private function send($fragment){ 179 | $curl = curl_init($this->token_url); 180 | curl_setopt($curl, CURLOPT_HEADER, false); 181 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); 182 | curl_setopt($curl, CURLOPT_POST, true); 183 | curl_setopt($curl, CURLOPT_POSTFIELDS, $fragment); 184 | curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_1); 185 | $response = json_decode(curl_exec($curl), true); 186 | $status = curl_getinfo($curl, CURLINFO_HTTP_CODE); 187 | if ($status == 400 && $response['error_description'] == 'expired authorization code') { 188 | //access code has been expired 189 | $this->set_error('new code required'); 190 | } elseif ( $status != 200 ) { 191 | $this->set_error("

Curl Error

URL : $this->token_url

Status : $status

response : error = " . $response['error'] . ", error_description = " . $response['error_description'] . "

curl_error : " . curl_error($curl) . "

curl_errno : " . curl_errno($curl) . "

"); 192 | } 193 | curl_close($curl); 194 | return($response); 195 | } 196 | 197 | private function save_to_filesystem(){ 198 | $array_cache = array("access_token", "refresh_token", "instance_url"); 199 | foreach($array_cache as $k => $v){ 200 | $fp = fopen($this->cache_dir . "/" . $v, "w"); 201 | if ($fp == FALSE){ 202 | $this->set_error("Failed to open " . $this->cache_dir . "/" . $v . " in write mode."); 203 | return; 204 | } 205 | fwrite($fp, $this->$v); 206 | fclose($fp); 207 | } 208 | } 209 | 210 | private function save_to_session(){ 211 | $array_cache = array("access_token", "refresh_token", "instance_url"); 212 | foreach($array_cache as $k => $v){ 213 | $_SESSION[$v] = $this->$v; 214 | } 215 | $_SESSION['created_at'] = time(); 216 | } 217 | 218 | private function set_error($error_msg){ 219 | $this->error = TRUE; 220 | array_push($this->error_msg, $error_msg); 221 | } 222 | 223 | public function trace_error(){ 224 | if ($this->error){ 225 | foreach ($this->error_msg as $k => $v){ 226 | print '

' . $v . '

'; 227 | } 228 | } 229 | } 230 | } 231 | ?> 232 | --------------------------------------------------------------------------------