3 namespace Friendica\Security\OAuth1;
5 use Friendica\Security\FKOAuthDataStore;
6 use Friendica\Security\OAuth1\Signature;
10 protected $timestamp_threshold = 300; // in seconds, five minutes
11 protected $version = '1.0'; // hi blaine
12 /** @var Signature\OAuthSignatureMethod[] */
13 protected $signature_methods = [];
15 /** @var FKOAuthDataStore */
16 protected $data_store;
18 function __construct(FKOAuthDataStore $data_store)
20 $this->data_store = $data_store;
23 public function add_signature_method(Signature\OAuthSignatureMethod $signature_method)
25 $this->signature_methods[$signature_method->get_name()] =
29 // high level functions
32 * process a request_token request
33 * returns the request token on success
35 * @param OAuthRequest $request
37 * @return OAuthToken|null
38 * @throws OAuthException
40 public function fetch_request_token(OAuthRequest $request)
42 $this->get_version($request);
44 $consumer = $this->get_consumer($request);
46 // no token required for the initial token request
49 $this->check_signature($request, $consumer, $token);
52 $callback = $request->get_parameter('oauth_callback');
53 $new_token = $this->data_store->new_request_token($consumer, $callback);
59 * process an access_token request
60 * returns the access token on success
62 * @param OAuthRequest $request
65 * @throws OAuthException
67 public function fetch_access_token(OAuthRequest $request)
69 $this->get_version($request);
71 $consumer = $this->get_consumer($request);
73 // requires authorized request token
74 $token = $this->get_token($request, $consumer, "request");
76 $this->check_signature($request, $consumer, $token);
79 $verifier = $request->get_parameter('oauth_verifier');
80 $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
86 * verify an api call, checks all the parameters
88 * @param OAuthRequest $request
91 * @throws OAuthException
93 public function verify_request(OAuthRequest $request)
95 $this->get_version($request);
96 $consumer = $this->get_consumer($request);
97 $token = $this->get_token($request, $consumer, "access");
98 $this->check_signature($request, $consumer, $token);
99 return [$consumer, $token];
102 // Internals from here
107 * @param OAuthRequest $request
110 * @throws OAuthException
112 private function get_version(OAuthRequest $request)
114 $version = $request->get_parameter("oauth_version");
116 // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
117 // Chapter 7.0 ("Accessing Protected Ressources")
120 if ($version !== $this->version) {
121 throw new OAuthException("OAuth version '$version' not supported");
127 * figure out the signature with some defaults
129 * @param OAuthRequest $request
131 * @return Signature\OAuthSignatureMethod
132 * @throws OAuthException
134 private function get_signature_method(OAuthRequest $request)
137 @$request->get_parameter("oauth_signature_method");
139 if (!$signature_method) {
140 // According to chapter 7 ("Accessing Protected Ressources") the signature-method
141 // parameter is required, and we can't just fallback to PLAINTEXT
142 throw new OAuthException('No signature method parameter. This parameter is required');
147 array_keys($this->signature_methods)
149 throw new OAuthException(
150 "Signature method '$signature_method' not supported " .
151 "try one of the following: " .
152 implode(", ", array_keys($this->signature_methods))
155 return $this->signature_methods[$signature_method];
159 * try to find the consumer for the provided request's consumer key
161 * @param OAuthRequest $request
163 * @return OAuthConsumer
164 * @throws OAuthException
166 private function get_consumer(OAuthRequest $request)
168 $consumer_key = @$request->get_parameter("oauth_consumer_key");
169 if (!$consumer_key) {
170 throw new OAuthException("Invalid consumer key");
173 $consumer = $this->data_store->lookup_consumer($consumer_key);
175 throw new OAuthException("Invalid consumer");
182 * try to find the token for the provided request's token key
184 * @param OAuthRequest $request
186 * @param string $token_type
188 * @return OAuthToken|null
189 * @throws OAuthException
191 private function get_token(OAuthRequest &$request, $consumer, $token_type = "access")
193 $token_field = @$request->get_parameter('oauth_token');
194 $token = $this->data_store->lookup_token(
200 throw new OAuthException("Invalid $token_type token: $token_field");
206 * all-in-one function to check the signature on a request
207 * should guess the signature method appropriately
209 * @param OAuthRequest $request
210 * @param OAuthConsumer $consumer
211 * @param OAuthToken|null $token
213 * @throws OAuthException
215 private function check_signature(OAuthRequest $request, OAuthConsumer $consumer, OAuthToken $token = null)
217 // this should probably be in a different method
218 $timestamp = @$request->get_parameter('oauth_timestamp');
219 $nonce = @$request->get_parameter('oauth_nonce');
221 $this->check_timestamp($timestamp);
222 $this->check_nonce($consumer, $token, $nonce, $timestamp);
224 $signature_method = $this->get_signature_method($request);
226 $signature = $request->get_parameter('oauth_signature');
227 $valid_sig = $signature_method->check_signature(
235 throw new OAuthException("Invalid signature");
240 * check that the timestamp is new enough
242 * @param int $timestamp
244 * @throws OAuthException
246 private function check_timestamp($timestamp)
249 throw new OAuthException(
250 'Missing timestamp parameter. The parameter is required'
253 // verify that timestamp is recentish
255 if (abs($now - $timestamp) > $this->timestamp_threshold) {
256 throw new OAuthException(
257 "Expired timestamp, yours $timestamp, ours $now"
263 * check that the nonce is not repeated
265 * @param OAuthConsumer $consumer
266 * @param OAuthToken $token
267 * @param string $nonce
268 * @param int $timestamp
270 * @throws OAuthException
272 private function check_nonce(OAuthConsumer $consumer, OAuthToken $token, $nonce, int $timestamp)
275 throw new OAuthException(
276 'Missing nonce parameter. The parameter is required'
279 // verify that the nonce is uniqueish
280 $found = $this->data_store->lookup_nonce(
287 throw new OAuthException("Nonce already used: $nonce");