├── .gitmodules
├── .htaccess
├── index.php
├── lib
├── config.php
└── relmeauth.php
└── readme.markdown
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "lib/cassis"]
2 | path = lib/cassis
3 | url = https://github.com/tantek/cassis.git
4 | [submodule "lib/tmhOAuth"]
5 | path = lib/tmhOAuth
6 | url = https://github.com/themattharris/tmhOAuth.git
7 |
--------------------------------------------------------------------------------
/.htaccess:
--------------------------------------------------------------------------------
1 |
You are logged in as using 84 | . logout?
85 | 86 | 87 |Congratulations. You've unlocked RelMeAuth level 2 and can now post updates.
88 | 89 | 94 | 95 | 96 |Tweeting...
97 | tmhOAuth; 98 | $tmhOAuth->request('POST', $tmhOAuth->url('statuses/update'), array( 99 | 'status' => $_POST['post'] 100 | )); 101 | ?> 102 |Twitter's API says:
103 | response['code'] == 200) { 104 | $tmhOAuth->pr(json_decode($tmhOAuth->response['response'])); 105 | } else { 106 | $tmhOAuth->pr(htmlentities($tmhOAuth->response['response'])); 107 | } 108 | } // /user posted 109 | } else { // no write access yet, so encourage user to upgrade 110 | ?> 111 |Congratulations. You've unlocked RelMeAuth level 1.
112 | 113 | 114 |Would you like to try posting? Allow RelMeAuth to update your Twitter.
115 | 120 | printError(); ?> 124 |This is a working prototype of RelMeAuth.
125 |This is purely a test user interface. If this had been an actual user interface, 126 | you wouldn't be wondering what the hell is going on, what is "my domain", 127 | who am I, and why do I exist.
128 |This is only a test.
129 |Enter your personal web address, click Sign In, and see what happens.
130 | 131 | 142 | 143 |This checkbox is a metaphorical big red button you're supposed to try ignoring, or dare to click. It's only here because the OAuth allow permission page of the presumed destination doesn't have it, which is really where it should be (so we don't have to presume the destination).
144 | 145 |It is likely there are still errors and any issues should be reported on the 146 | GitHub Project Page. This code is written by 147 | @themattharris and @t. It 148 | uses a modified OAuth PHP library.
149 | 150 | 151 | 152 | 195 | 196 | 197 | -------------------------------------------------------------------------------- /lib/config.php: -------------------------------------------------------------------------------- 1 | array( 5 | 'keys' => array( 6 | 'consumer_key' => 'YOUR_CONSUMER_KEY', // YOUR_CONSUMER_KEY 7 | 'consumer_secret' => 'YOUR_CONSUMER_SECRET', // YOUR_CONSUMER_SECRET 8 | ), 9 | 'urls' => array( 10 | 'request' => 'https://api.twitter.com/oauth/request_token', 11 | 'authenticate' => 'https://api.twitter.com/oauth/authenticate', // auto 12 | 'authorize' => 'https://api.twitter.com/oauth/authorize', // ask 13 | 'access' => 'https://api.twitter.com/oauth/access_token', 14 | 'verify' => 'https://api.twitter.com/1/account/verify_credentials.json', 15 | ), 16 | 'verify' => array( 17 | 'url' => 'url', 18 | 'name' => 'url' 19 | ), 20 | 'rtrimprofile' => '/', 21 | 'ltrimdomain' => 'www.' 22 | ), 23 | 'identi.ca' => array( 24 | 'keys' => array( 25 | 'consumer_key' => 'YOUR_CONSUMER_KEY', // YOUR_CONSUMER_KEY 26 | 'consumer_secret' => 'YOUR_CONSUMER_SECRET', // YOUR_CONSUMER_SECRET 27 | ), 28 | 'urls' => array( 29 | 'request' => 'https://api.identi.ca/oauth/request_token', // http://identi.ca/api/oauth/request_token 30 | 'authenticate' => 'https://api.identi.ca/oauth/authenticate', 31 | 'authorize' => 'https://api.identi.ca/oauth/authorize', // http://identi.ca/api/oauth/authorize 32 | 'access' => 'https://api.identi.ca/oauth/access_token', // http://identi.ca/api/oauth/access_token 33 | 'verify' => 'https://api.identi.ca/1/account/verify_credentials.json', 34 | ), 35 | 'verify' => array( 36 | 'url' => 'url', 37 | 'name' => 'url' 38 | ), 39 | 'rtrimprofile' => '/', 40 | 'ltrimdomain' => 'www.' 41 | ) 42 | ); 43 | 44 | ?> -------------------------------------------------------------------------------- /lib/relmeauth.php: -------------------------------------------------------------------------------- 1 | $v) { 8 | unset($process[$key][$k]); 9 | if (is_array($v)) { 10 | $process[$key][stripslashes($k)] = $v; 11 | $process[] = &$process[$key][stripslashes($k)]; 12 | } else { 13 | $process[$key][stripslashes($k)] = stripslashes($v); 14 | } 15 | } 16 | } 17 | unset($process); 18 | } 19 | 20 | ob_start(); require_once dirname(__FILE__) . '/cassis/cassis.js'; ob_end_clean(); 21 | require dirname(__FILE__) . '/tmhOAuth/tmhOAuth.php'; 22 | require dirname(__FILE__) . '/config.php'; 23 | 24 | class relmeauth { 25 | function __construct() { 26 | session_start(); 27 | $this->tmhOAuth = new tmhOAuth(array()); 28 | } 29 | 30 | function is_loggedin() { 31 | // TODO: should have a timestamp expiry in here. 32 | return (isset($_SESSION['relmeauth']['name'])); 33 | } 34 | 35 | function create_from_session() { 36 | global $providers; 37 | 38 | $config = $providers[$_SESSION['relmeauth']['provider']]; 39 | 40 | // create tmhOAuth from session info 41 | $this->tmhOAuth = new tmhOAuth(array( 42 | 'consumer_key' => $config['keys']['consumer_key'], 43 | 'consumer_secret' => $config['keys']['consumer_secret'], 44 | 'user_token' => $_SESSION['relmeauth']['access']['oauth_token'], 45 | 'user_secret' => $_SESSION['relmeauth']['access']['oauth_token_secret'] 46 | )); 47 | } 48 | 49 | function main($user_url, $askwrite) { 50 | // first try to authenticate directly with the URL given 51 | if ($this->is_provider($user_url)) { 52 | $_SESSION['relmeauth']['direct'] = true; 53 | if ($this->authenticate_url($user_url, $askwrite)) { 54 | return true; // bail once something claims to authenticate 55 | } 56 | unset($_SESSION['relmeauth']['direct']); 57 | } 58 | 59 | // get the rel-me URLs from the given site 60 | $source_rels = $this->discover($user_url); 61 | 62 | if ($source_rels==false || count($source_rels) == 0) { 63 | return false; // no rel-me links found, bail 64 | } 65 | 66 | // separate them into external and same domain 67 | $external_rels = array(); 68 | $local_rels = array(); 69 | $user_site = parse_url($user_url); 70 | 71 | foreach ($source_rels as $source_rel => $details) : 72 | $provider = parse_url($source_rel); 73 | if ($provider['host'] == $user_site['host']) { 74 | $local_rels[$source_rel] = $details; 75 | } else { 76 | $external_rels[$source_rel] = $details; 77 | } 78 | endforeach; // source_rels 79 | 80 | // see if any of the external rel-me URLs reciprocate - check rels in order 81 | // and then try authing it. needs to maintain more session state to resume. 82 | foreach ($external_rels as $external_rel => $details): 83 | // only bother to confirm rel-me etc. if we know how to auth the dest. 84 | if ($this->is_provider($external_rel) && 85 | $this->confirm_rel($user_url, $external_rel)) { 86 | // We could keep this as a URL we actually try to auth, for debugging 87 | if ($this->authenticate_url($external_rel, $askwrite)) { 88 | return true; // bail once something claims to authenticate 89 | } 90 | } 91 | endforeach; // external_rels 92 | 93 | $source_rels = array_merge($local_rels, $external_rels); 94 | $source2_tried = array(); 95 | 96 | // no external_rels, or none of them reciprocated or authed. try next level. 97 | foreach ($source_rels as $source_rel => $details) : 98 | // try rel-me-authing $source_rel, 99 | // and test its respective external $source2_urls 100 | // to match against $source_rel OR $user_url. 101 | 102 | $source_rel_confirmed = 103 | strpos($source_rel, $user_url)===0 || 104 | $this->confirm_rel($user_url, $source_rel); 105 | // if $source_rel is a confirmed rel-me itself, 106 | // then we'll allow for 2nd level to confirm to it 107 | 108 | // then check its external_rels 109 | $source2_rels = $this->discover($source_rel); 110 | if ($source2_rels!=false) { 111 | foreach ($source2_rels as $source2_rel => $details) : 112 | $provider = parse_url($source2_rel); 113 | if ($provider['host'] != $user_site['host'] && 114 | $this->is_provider($source2_rel)) 115 | { 116 | $source2_tried[$source2_rel] = $details; 117 | if ((!$source_rel_confirmed && 118 | $this->confirm_rel($user_url, $source2_rel)) || 119 | ($source_rel_confirmed && 120 | $this->confirms_rel($user_url, $source_rel, $source2_rel))) 121 | { 122 | // could keep this as a URL we actually try to auth, for debugging 123 | if ($source_rel_confirmed) { 124 | $_SESSION['relmeauth']['url2'] = $source_rel; 125 | } 126 | if ($this->authenticate_url($source2_rel, $askwrite)) { 127 | // this exits if it succeeds. next statement unnecessary. 128 | return true; // bail once something claims to authenticate 129 | } 130 | $_SESSION['relmeauth']['url2'] = ''; 131 | } 132 | } 133 | endforeach; // source_rels 134 | } 135 | 136 | // if successful, should have returned true, which can be returned 137 | endforeach; // source_rels 138 | 139 | /* 140 | //debugging 141 | $debugurls = $this->discover('http://twitter.com/kevinmarks/'); 142 | 143 | //end debugging 144 | */ 145 | 146 | // otherwise, no URLs worked. 147 | $source_rels = implode(', ', array_keys($source_rels)) . 148 | ($source2_tried && count($source2_tried)>=0 ? ', ' . 149 | implode(', ', array_keys($source2_tried)) : '') 150 | /* 151 | . 152 | ($debugurls && count($debugurls)>=0 ? '. debug: ' . 153 | implode(', ', array_keys($debugurls)) : '') 154 | */ 155 | ; 156 | 157 | $this->error('None of your providers are supported. Tried ' . $source_rels . '.'); 158 | 159 | return false; 160 | 161 | /* 162 | // old code that first confirmed all rel-me links, and then tried as a batch 163 | // see if any of the relmes match back - we check the rels in the order 164 | // they are listed in the HTML 165 | $confirmed_rels = $this->confirm_rels($user_url, $source_rels); 166 | if ($confirmed_rels != false) { 167 | return $this->authenticate($confirmed_rels); 168 | } else { 169 | // error message will have already been set 170 | return false; 171 | } 172 | */ 173 | } 174 | 175 | function request($keys, $method, $url, $params=array(), $useauth=true) { 176 | $this->tmhOAuth = new tmhOAuth(array()); 177 | 178 | $this->tmhOAuth->config['consumer_key'] = $keys['consumer_key']; 179 | $this->tmhOAuth->config['consumer_secret'] = $keys['consumer_secret']; 180 | $this->tmhOAuth->config['user_token'] = @$keys['user_token']; 181 | $this->tmhOAuth->config['user_secret'] = @$keys['user_secret']; 182 | $code = $this->tmhOAuth->request( 183 | $method, 184 | $url, 185 | $params, 186 | $useauth 187 | ); 188 | 189 | return ( $code == 200 ); 190 | } 191 | 192 | 193 | /** 194 | * check to see if we know how to OAuth a URL 195 | * 196 | * @return whether or not it's a provider we know how to deal with 197 | * @author Tantek Çelik 198 | */ 199 | function is_provider($confirmed_rel) { 200 | global $providers; 201 | 202 | $provider = parse_url($confirmed_rel); 203 | if (array_key_exists($provider['host'], $providers)) { 204 | return true; 205 | } 206 | if (strpos($provider['host'], 'www.')===0) { 207 | $provider['host'] = substr($provider['host'],4); 208 | if (array_key_exists($provider['host'], $providers) && 209 | $providers[$provider['host']]['ltrimdomain'] == 'www.') 210 | { 211 | return true; 212 | } 213 | } 214 | return false; 215 | } 216 | 217 | /** 218 | * Wrapper for the OAuth authentication process for a URL 219 | * 220 | * @return false if authentication failed 221 | * @author Matt Harris and Tantek Çelik 222 | */ 223 | function authenticate_url($confirmed_rel, $askwrite) { 224 | global $providers; 225 | 226 | if (!$this->is_provider($confirmed_rel)) 227 | return false; 228 | 229 | $provider = parse_url($confirmed_rel); 230 | $config = $providers[ $provider['host'] ]; 231 | $ok = $this->request( 232 | $config['keys'], 233 | 'GET', 234 | $config['urls']['request'], 235 | array( 236 | 'oauth_callback' => $this->here(), 237 | 'x_auth_access_type' => ($askwrite ? 'write' : 'read'), // http://dev.twitter.com/doc/post/oauth/request_token 238 | ) 239 | ); 240 | 241 | if ($ok) { 242 | // need these later 243 | $relpath = $provider['path']; 244 | $user = $this->tmhOAuth->extract_params($this->tmhOAuth->response['response']); 245 | 246 | $_SESSION['relmeauth']['provider'] = $provider['host']; 247 | $_SESSION['relmeauth']['secret'] = $user['oauth_token_secret']; 248 | $_SESSION['relmeauth']['token'] = $user['oauth_token']; 249 | $url = ($askwrite ? $config['urls']['authorize'] 250 | : $config['urls']['authenticate']) . '?' 251 | . "oauth_token={$user['oauth_token']}"; 252 | $this->redirect($url); 253 | return true; 254 | } else { 255 | $this->error("There was a problem communicating with {$provider['host']}. Error {$this->tmhOAuth->response['code']}. Please try later."); 256 | } 257 | 258 | return false; 259 | } 260 | 261 | /** 262 | * Wrapper for the OAuth authentication process 263 | * 264 | * @return false upon failure 265 | * @author Matt Harris and Tantek Çelik 266 | */ 267 | function authenticate($confirmed_rels) { 268 | global $providers; 269 | 270 | foreach ($confirmed_rels as $host => $details) : 271 | if (authenticate_url($host)) 272 | return true; 273 | endforeach; // confirmed_rels 274 | 275 | $this->error('None of your providers are supported. Tried ' . implode(', ', array_keys($confirmed_rels)) . '.'); 276 | return false; 277 | } 278 | 279 | function complete_oauth( $verifier ) { 280 | global $providers; 281 | 282 | if ( ! array_key_exists($_SESSION['relmeauth']['provider'], $providers) ) { 283 | $this->error('None of your providers are supported, or you might have cookies disabled. Make sure your browser preferences are set to accept cookies and try again.'); 284 | return false; 285 | } 286 | 287 | $config = $providers[$_SESSION['relmeauth']['provider']]; 288 | $ok = $this->request( 289 | array_merge( 290 | $config['keys'], 291 | array( 292 | 'user_token' => $_SESSION['relmeauth']['token'], 293 | 'user_secret' => $_SESSION['relmeauth']['secret'] 294 | ) 295 | ), 296 | 'GET', 297 | $config['urls']['access'], 298 | array( 299 | 'oauth_verifier' => $verifier 300 | ) 301 | ); 302 | unset($_SESSION['relmeauth']['token']); 303 | unset($_SESSION['relmeauth']['secret']); 304 | 305 | if ($ok) { 306 | // get the users token and secret 307 | $_SESSION['relmeauth']['access'] = $this->tmhOAuth->extract_params($this->tmhOAuth->response['response']); 308 | 309 | // FIXME: validate this is the user who requested. 310 | // At the moment if I use another users URL that rel=me to Twitter for example, it 311 | // will work for me - because all we do is go 'oh Twitter, sure, login there and you're good to go 312 | // the rel=me bit doesn't get confirmed it belongs to the user 313 | $this->verify( $config ); 314 | $this->redirect(); 315 | } 316 | $this->error("There was a problem authenticating with {$provider['host']}. Error {$this->tmhOAuth->response['code']}. Please try later."); 317 | return false; 318 | } 319 | 320 | function verify( &$config ) { 321 | global $providers; 322 | $config = $providers[$_SESSION['relmeauth']['provider']]; 323 | 324 | $ok = $this->request( 325 | array_merge( 326 | $config['keys'], 327 | array( 328 | 'user_token' => $_SESSION['relmeauth']['access']['oauth_token'], 329 | 'user_secret' => $_SESSION['relmeauth']['access']['oauth_token_secret'] 330 | ) 331 | ), 332 | 'GET', 333 | $config['urls']['verify'] 334 | ); 335 | 336 | $creds = json_decode($this->tmhOAuth->response['response'], true); 337 | 338 | $given = self::normalise_url($_SESSION['relmeauth']['url']); 339 | $found = self::normalise_url(self::expand_tco($creds[ $config['verify']['url'] ])); 340 | 341 | $_SESSION['relmeauth']['debug']['verify']['given'] = $given; 342 | $_SESSION['relmeauth']['debug']['verify']['found'] = $found; 343 | 344 | if ( $given != $found && 345 | array_key_exists('url2', $_SESSION['relmeauth'])) 346 | { 347 | $given = self::normalise_url($_SESSION['relmeauth']['url2']); 348 | } 349 | 350 | if ( $given == $found || 351 | ($this->is_provider($given) && $_SESSION['relmeauth']['direct'])) 352 | { 353 | $_SESSION['relmeauth']['name'] = $creds[ $config['verify']['name'] ]; 354 | return true; 355 | } else { 356 | // destroy everything 357 | $provider = $_SESSION['relmeauth']['provider']; 358 | // unset($_SESSION['relmeauth']); 359 | $this->error("That isn't you! If it really is you, try signing out of {$provider}. Entered $given (". @$_SESSION['relmeauth']['url2'] . "), found $found."); 360 | return false; 361 | } 362 | } 363 | 364 | function error($message) { 365 | if ( ! isset( $_SESSION['relmeauth']['error'] ) ) { 366 | $_SESSION['relmeauth']['error'] = $message; 367 | } else { 368 | $_SESSION['relmeauth']['error'] .= ' ' . $message; 369 | } 370 | } 371 | 372 | /** 373 | * Print the last error message if there is one. 374 | * 375 | * @return void 376 | * @author Matt Harris 377 | */ 378 | function printError() { 379 | if ( isset( $_SESSION['relmeauth']['error'] ) ) { 380 | echo '