OAuthServer.php 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. <?php
  2. class OAuthServer {
  3. protected $timestamp_threshold = 300; // in seconds, five minutes
  4. protected $version = '1.0'; // hi blaine
  5. protected $signature_methods = array();
  6. protected $data_store;
  7. function __construct($data_store) {
  8. $this->data_store = $data_store;
  9. }
  10. public function add_signature_method($signature_method) {
  11. $this->signature_methods[$signature_method->get_name()] =
  12. $signature_method;
  13. }
  14. // high level functions
  15. /**
  16. * process a request_token request
  17. * returns the request token on success
  18. */
  19. public function fetch_request_token(&$request) {
  20. $this->get_version($request);
  21. $consumer = $this->get_consumer($request);
  22. // no token required for the initial token request
  23. $token = NULL;
  24. $this->check_signature($request, $consumer, $token);
  25. // Rev A change
  26. $callback = $request->get_parameter('oauth_callback');
  27. $new_token = $this->data_store->new_request_token($consumer, $callback);
  28. return $new_token;
  29. }
  30. /**
  31. * process an access_token request
  32. * returns the access token on success
  33. */
  34. public function fetch_access_token(&$request) {
  35. $this->get_version($request);
  36. $consumer = $this->get_consumer($request);
  37. // requires authorized request token
  38. $token = $this->get_token($request, $consumer, "request");
  39. $this->check_signature($request, $consumer, $token);
  40. // Rev A change
  41. $verifier = $request->get_parameter('oauth_verifier');
  42. $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
  43. return $new_token;
  44. }
  45. /**
  46. * verify an api call, checks all the parameters
  47. */
  48. public function verify_request(&$request) {
  49. $this->get_version($request);
  50. $consumer = $this->get_consumer($request);
  51. $token = $this->get_token($request, $consumer, "access");
  52. $this->check_signature($request, $consumer, $token);
  53. return array($consumer, $token);
  54. }
  55. // Internals from here
  56. /**
  57. * version 1
  58. */
  59. private function get_version(&$request) {
  60. $version = $request->get_parameter("oauth_version");
  61. if (!$version) {
  62. // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
  63. // Chapter 7.0 ("Accessing Protected Ressources")
  64. $version = '1.0';
  65. }
  66. if ($version !== $this->version) {
  67. throw new OAuthException("OAuth version '$version' not supported");
  68. }
  69. return $version;
  70. }
  71. /**
  72. * figure out the signature with some defaults
  73. */
  74. private function get_signature_method($request) {
  75. $signature_method = $request instanceof OAuthRequest
  76. ? $request->get_parameter("oauth_signature_method")
  77. : NULL;
  78. if (!$signature_method) {
  79. // According to chapter 7 ("Accessing Protected Ressources") the signature-method
  80. // parameter is required, and we can't just fallback to PLAINTEXT
  81. throw new OAuthException('No signature method parameter. This parameter is required');
  82. }
  83. if (!in_array($signature_method,
  84. array_keys($this->signature_methods))) {
  85. throw new OAuthException(
  86. "Signature method '$signature_method' not supported " .
  87. "try one of the following: " .
  88. implode(", ", array_keys($this->signature_methods))
  89. );
  90. }
  91. return $this->signature_methods[$signature_method];
  92. }
  93. /**
  94. * try to find the consumer for the provided request's consumer key
  95. */
  96. private function get_consumer($request) {
  97. $consumer_key = $request instanceof OAuthRequest
  98. ? $request->get_parameter("oauth_consumer_key")
  99. : NULL;
  100. if (!$consumer_key) {
  101. throw new OAuthException("Invalid consumer key");
  102. }
  103. $consumer = $this->data_store->lookup_consumer($consumer_key);
  104. if (!$consumer) {
  105. throw new OAuthException("Invalid consumer");
  106. }
  107. return $consumer;
  108. }
  109. /**
  110. * try to find the token for the provided request's token key
  111. */
  112. private function get_token($request, $consumer, $token_type="access") {
  113. $token_field = $request instanceof OAuthRequest
  114. ? $request->get_parameter('oauth_token')
  115. : NULL;
  116. $token = $this->data_store->lookup_token(
  117. $consumer, $token_type, $token_field
  118. );
  119. if (!$token) {
  120. throw new OAuthException("Invalid $token_type token: $token_field");
  121. }
  122. return $token;
  123. }
  124. /**
  125. * all-in-one function to check the signature on a request
  126. * should guess the signature method appropriately
  127. */
  128. private function check_signature($request, $consumer, $token) {
  129. // this should probably be in a different method
  130. $timestamp = $request instanceof OAuthRequest
  131. ? $request->get_parameter('oauth_timestamp')
  132. : NULL;
  133. $nonce = $request instanceof OAuthRequest
  134. ? $request->get_parameter('oauth_nonce')
  135. : NULL;
  136. $this->check_timestamp($timestamp);
  137. $this->check_nonce($consumer, $token, $nonce, $timestamp);
  138. $signature_method = $this->get_signature_method($request);
  139. $signature = $request->get_parameter('oauth_signature');
  140. $valid_sig = $signature_method->check_signature(
  141. $request,
  142. $consumer,
  143. $token,
  144. $signature
  145. );
  146. if (!$valid_sig) {
  147. throw new OAuthException("Invalid signature");
  148. }
  149. }
  150. /**
  151. * check that the timestamp is new enough
  152. */
  153. private function check_timestamp($timestamp) {
  154. if( ! $timestamp )
  155. throw new OAuthException(
  156. 'Missing timestamp parameter. The parameter is required'
  157. );
  158. // verify that timestamp is recentish
  159. $now = time();
  160. if (abs($now - $timestamp) > $this->timestamp_threshold) {
  161. throw new OAuthException(
  162. "Expired timestamp, yours $timestamp, ours $now"
  163. );
  164. }
  165. }
  166. /**
  167. * check that the nonce is not repeated
  168. */
  169. private function check_nonce($consumer, $token, $nonce, $timestamp) {
  170. if( ! $nonce )
  171. throw new OAuthException(
  172. 'Missing nonce parameter. The parameter is required'
  173. );
  174. // verify that the nonce is uniqueish
  175. $found = $this->data_store->lookup_nonce(
  176. $consumer,
  177. $token,
  178. $nonce,
  179. $timestamp
  180. );
  181. if ($found) {
  182. throw new OAuthException("Nonce already used: $nonce");
  183. }
  184. }
  185. }