3 namespace Friendica\Security\OAuth1;
5 use Friendica\Security\FKOAuthDataStore;
8 use OAuthSignatureMethod;
13 protected $timestamp_threshold = 300; // in seconds, five minutes
14 protected $version = '1.0'; // hi blaine
15 /** @var \Friendica\Security\OAuth1\OAuthSignatureMethod[] */
16 protected $signature_methods = [];
18 /** @var FKOAuthDataStore */
19 protected $data_store;
21 function __construct(FKOAuthDataStore $data_store)
23 $this->data_store = $data_store;
26 public function add_signature_method(\Friendica\Security\OAuth1\OAuthSignatureMethod $signature_method)
28 $this->signature_methods[$signature_method->get_name()] =
32 // high level functions
35 * process a request_token request
36 * returns the request token on success
38 * @param \Friendica\Security\OAuth1\OAuthRequest $request
40 * @return \Friendica\Security\OAuth1\OAuthToken|null
41 * @throws OAuthException
43 public function fetch_request_token(\Friendica\Security\OAuth1\OAuthRequest $request)
45 $this->get_version($request);
47 $consumer = $this->get_consumer($request);
49 // no token required for the initial token request
52 $this->check_signature($request, $consumer, $token);
55 $callback = $request->get_parameter('oauth_callback');
56 $new_token = $this->data_store->new_request_token($consumer, $callback);
62 * process an access_token request
63 * returns the access token on success
65 * @param \Friendica\Security\OAuth1\OAuthRequest $request
68 * @throws OAuthException
70 public function fetch_access_token(\Friendica\Security\OAuth1\OAuthRequest $request)
72 $this->get_version($request);
74 $consumer = $this->get_consumer($request);
76 // requires authorized request token
77 $token = $this->get_token($request, $consumer, "request");
79 $this->check_signature($request, $consumer, $token);
82 $verifier = $request->get_parameter('oauth_verifier');
83 $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
89 * verify an api call, checks all the parameters
91 * @param \Friendica\Security\OAuth1\OAuthRequest $request
94 * @throws OAuthException
96 public function verify_request(\Friendica\Security\OAuth1\OAuthRequest $request)
98 $this->get_version($request);
99 $consumer = $this->get_consumer($request);
100 $token = $this->get_token($request, $consumer, "access");
101 $this->check_signature($request, $consumer, $token);
102 return [$consumer, $token];
105 // Internals from here
110 * @param \Friendica\Security\OAuth1\OAuthRequest $request
113 * @throws OAuthException
115 private function get_version(\Friendica\Security\OAuth1\OAuthRequest $request)
117 $version = $request->get_parameter("oauth_version");
119 // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
120 // Chapter 7.0 ("Accessing Protected Ressources")
123 if ($version !== $this->version) {
124 throw new OAuthException("OAuth version '$version' not supported");
130 * figure out the signature with some defaults
132 * @param \Friendica\Security\OAuth1\OAuthRequest $request
134 * @return \Friendica\Security\OAuth1\OAuthSignatureMethod
135 * @throws OAuthException
137 private function get_signature_method(\Friendica\Security\OAuth1\OAuthRequest $request)
140 @$request->get_parameter("oauth_signature_method");
142 if (!$signature_method) {
143 // According to chapter 7 ("Accessing Protected Ressources") the signature-method
144 // parameter is required, and we can't just fallback to PLAINTEXT
145 throw new OAuthException('No signature method parameter. This parameter is required');
150 array_keys($this->signature_methods)
152 throw new OAuthException(
153 "Signature method '$signature_method' not supported " .
154 "try one of the following: " .
155 implode(", ", array_keys($this->signature_methods))
158 return $this->signature_methods[$signature_method];
162 * try to find the consumer for the provided request's consumer key
164 * @param \Friendica\Security\OAuth1\OAuthRequest $request
166 * @return \Friendica\Security\OAuth1\OAuthConsumer
167 * @throws OAuthException
169 private function get_consumer(\Friendica\Security\OAuth1\OAuthRequest $request)
171 $consumer_key = @$request->get_parameter("oauth_consumer_key");
172 if (!$consumer_key) {
173 throw new OAuthException("Invalid consumer key");
176 $consumer = $this->data_store->lookup_consumer($consumer_key);
178 throw new OAuthException("Invalid consumer");
185 * try to find the token for the provided request's token key
187 * @param \Friendica\Security\OAuth1\OAuthRequest $request
189 * @param string $token_type
191 * @return \Friendica\Security\OAuth1\OAuthToken|null
192 * @throws OAuthException
194 private function get_token(\Friendica\Security\OAuth1\OAuthRequest &$request, $consumer, $token_type = "access")
196 $token_field = @$request->get_parameter('oauth_token');
197 $token = $this->data_store->lookup_token(
203 throw new OAuthException("Invalid $token_type token: $token_field");
209 * all-in-one function to check the signature on a request
210 * should guess the signature method appropriately
212 * @param \Friendica\Security\OAuth1\OAuthRequest $request
213 * @param \Friendica\Security\OAuth1\OAuthConsumer $consumer
214 * @param \Friendica\Security\OAuth1\OAuthToken|null $token
216 * @throws OAuthException
218 private function check_signature(\Friendica\Security\OAuth1\OAuthRequest $request, \Friendica\Security\OAuth1\OAuthConsumer $consumer, \Friendica\Security\OAuth1\OAuthToken $token = null)
220 // this should probably be in a different method
221 $timestamp = @$request->get_parameter('oauth_timestamp');
222 $nonce = @$request->get_parameter('oauth_nonce');
224 $this->check_timestamp($timestamp);
225 $this->check_nonce($consumer, $token, $nonce, $timestamp);
227 $signature_method = $this->get_signature_method($request);
229 $signature = $request->get_parameter('oauth_signature');
230 $valid_sig = $signature_method->check_signature(
238 throw new OAuthException("Invalid signature");
243 * check that the timestamp is new enough
245 * @param int $timestamp
247 * @throws OAuthException
249 private function check_timestamp($timestamp)
252 throw new OAuthException(
253 'Missing timestamp parameter. The parameter is required'
256 // verify that timestamp is recentish
258 if (abs($now - $timestamp) > $this->timestamp_threshold) {
259 throw new OAuthException(
260 "Expired timestamp, yours $timestamp, ours $now"
266 * check that the nonce is not repeated
268 * @param \Friendica\Security\OAuth1\OAuthConsumer $consumer
269 * @param \Friendica\Security\OAuth1\OAuthToken $token
270 * @param string $nonce
271 * @param int $timestamp
273 * @throws OAuthException
275 private function check_nonce(\Friendica\Security\OAuth1\OAuthConsumer $consumer, \Friendica\Security\OAuth1\OAuthToken $token, $nonce, int $timestamp)
278 throw new OAuthException(
279 'Missing nonce parameter. The parameter is required'
282 // verify that the nonce is uniqueish
283 $found = $this->data_store->lookup_nonce(
290 throw new OAuthException("Nonce already used: $nonce");