Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

oauth.php

Zuletzt modifiziert: 02.04.2025, 15:02 - Dateigröße: 23.47 KiB


001  <?php
002  /**
003   *
004   * This file is part of the phpBB Forum Software package.
005   *
006   * @copyright (c) phpBB Limited <https://www.phpbb.com>
007   * @license GNU General Public License, version 2 (GPL-2.0)
008   *
009   * For full copyright and license information, please see
010   * the docs/CREDITS.txt file.
011   *
012   */
013   
014  namespace phpbb\auth\provider\oauth;
015   
016  use OAuth\Common\Http\Exception\TokenResponseException;
017  use OAuth\ServiceFactory;
018  use OAuth\Common\Consumer\Credentials;
019  use OAuth\Common\Service\ServiceInterface;
020  use OAuth\OAuth1\Service\AbstractService as OAuth1Service;
021  use OAuth\OAuth2\Service\AbstractService as OAuth2Service;
022  use phpbb\auth\provider\base;
023  use phpbb\auth\provider\db;
024  use phpbb\auth\provider\oauth\service\exception;
025  use phpbb\config\config;
026  use phpbb\db\driver\driver_interface;
027  use phpbb\di\service_collection;
028  use phpbb\event\dispatcher;
029  use phpbb\language\language;
030  use phpbb\request\request_interface;
031  use phpbb\user;
032   
033  /**
034   * OAuth authentication provider for phpBB3
035   */
036  class oauth extends base
037  {
038      /** @var config */
039      protected $config;
040   
041      /** @var driver_interface */
042      protected $db;
043   
044      /** @var db */
045      protected $db_auth;
046   
047      /** @var dispatcher */
048      protected $dispatcher;
049   
050      /** @var language */
051      protected $language;
052   
053      /** @var request_interface */
054      protected $request;
055   
056      /** @var service_collection */
057      protected $service_providers;
058   
059      /** @var user */
060      protected $user;
061   
062      /** @var string OAuth table: token storage */
063      protected $oauth_token_table;
064   
065      /** @var string OAuth table: state */
066      protected $oauth_state_table;
067   
068      /** @var string OAuth table: account association */
069      protected $oauth_account_table;
070   
071      /** @var string Users table */
072      protected $users_table;
073   
074      /** @var string phpBB root path */
075      protected $root_path;
076   
077      /** @var string php File extension */
078      protected $php_ext;
079   
080      /**
081       * Constructor.
082       *
083       * @param config                $config                    Config object
084       * @param driver_interface    $db                        Database object
085       * @param db            $db_auth                DB auth provider
086       * @param dispatcher            $dispatcher                Event dispatcher object
087       * @param language            $language                Language object
088       * @param request_interface    $request                Request object
089       * @param service_collection        $service_providers        OAuth providers service collection
090       * @param user                        $user                    User object
091       * @param string                            $oauth_token_table        OAuth table: token storage
092       * @param string                            $oauth_state_table        OAuth table: state
093       * @param string                            $oauth_account_table    OAuth table: account association
094       * @param string                            $users_table            User table
095       * @param string                            $root_path                phpBB root path
096       * @param string                            $php_ext                php File extension
097       */
098      public function __construct(
099          config $config,
100          driver_interface $db,
101          db $db_auth,
102          dispatcher $dispatcher,
103          language $language,
104          request_interface $request,
105          service_collection $service_providers,
106          user $user,
107          $oauth_token_table,
108          $oauth_state_table,
109          $oauth_account_table,
110          $users_table,
111          $root_path,
112          $php_ext
113      )
114      {
115          $this->config                = $config;
116          $this->db                    = $db;
117          $this->db_auth                = $db_auth;
118          $this->dispatcher            = $dispatcher;
119          $this->language                = $language;
120          $this->service_providers    = $service_providers;
121          $this->request                = $request;
122          $this->user                    = $user;
123   
124          $this->oauth_token_table    = $oauth_token_table;
125          $this->oauth_state_table    = $oauth_state_table;
126          $this->oauth_account_table    = $oauth_account_table;
127          $this->users_table            = $users_table;
128          $this->root_path            = $root_path;
129          $this->php_ext                = $php_ext;
130      }
131   
132      /**
133       * {@inheritdoc}
134       */
135      public function init()
136      {
137          // This does not test whether or not the key and secret provided are valid.
138          foreach ($this->service_providers as $service_provider)
139          {
140              $credentials = $service_provider->get_service_credentials();
141   
142              if (($credentials['key'] && !$credentials['secret']) || (!$credentials['key'] && $credentials['secret']))
143              {
144                  return $this->language->lang('AUTH_PROVIDER_OAUTH_ERROR_ELEMENT_MISSING');
145              }
146          }
147   
148          return false;
149      }
150   
151      /**
152       * {@inheritdoc}
153       */
154      public function login($username, $password)
155      {
156          // Temporary workaround for only having one authentication provider available
157          if (!$this->request->is_set('oauth_service'))
158          {
159              return $this->db_auth->login($username, $password);
160          }
161   
162          // Request the name of the OAuth service
163          $provider = $this->request->variable('oauth_service', '', false);
164          $service_name = $this->get_service_name($provider);
165   
166          if ($provider === '' || !$this->service_providers->offsetExists($service_name))
167          {
168              return [
169                  'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
170                  'error_msg'        => 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST',
171                  'user_row'        => ['user_id' => ANONYMOUS],
172              ];
173          }
174   
175          // Get the service credentials for the given service
176          $storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
177          $query = 'mode=login&login=external&oauth_service=' . $provider;
178   
179          try
180          {
181              /** @var OAuth1Service|OAuth2Service $service */
182              $service = $this->get_service($provider, $storage, $query);
183          }
184          catch (\Exception $e)
185          {
186              return [
187                  'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
188                  'error_msg'        => $e->getMessage(),
189                  'user_row'        => ['user_id' => ANONYMOUS],
190              ];
191          }
192   
193          if ($this->is_set_code($service))
194          {
195              $this->service_providers[$service_name]->set_external_service_provider($service);
196   
197              try
198              {
199                  $unique_id = $this->service_providers[$service_name]->perform_auth_login();
200              }
201              catch (exception $e)
202              {
203                  return [
204                      'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
205                      'error_msg'        => $e->getMessage(),
206                      'user_row'        => ['user_id' => ANONYMOUS],
207                  ];
208              }
209   
210              /**
211               * Check to see if this provider is already associated with an account.
212               *
213               * Enforcing a data type to make sure it are strings and not integers,
214               * so values are quoted in the SQL WHERE statement.
215               */
216              $data = [
217                  'provider'            => (string) utf8_strtolower($provider),
218                  'oauth_provider_id'    => (string) $unique_id
219              ];
220   
221              $sql = 'SELECT user_id 
222                  FROM ' . $this->oauth_account_table . '
223                  WHERE ' . $this->db->sql_build_array('SELECT', $data);
224              $result = $this->db->sql_query($sql);
225              $row = $this->db->sql_fetchrow($result);
226              $this->db->sql_freeresult($result);
227   
228              $redirect_data = array(
229                  'auth_provider'                => 'oauth',
230                  'login_link_oauth_service'    => $provider,
231              );
232   
233              /**
234               * Event is triggered before check if provider is already associated with an account
235               *
236               * @event core.oauth_login_after_check_if_provider_id_has_match
237               * @var array                row                User row
238               * @var array                data            Provider data
239               * @var    array                redirect_data    Data to be appended to the redirect url
240               * @var ServiceInterface    service            OAuth service
241               * @since 3.2.3-RC1
242               * @changed 3.2.6-RC1                        Added redirect_data
243               */
244              $vars = [
245                  'row',
246                  'data',
247                  'redirect_data',
248                  'service',
249              ];
250              extract($this->dispatcher->trigger_event('core.oauth_login_after_check_if_provider_id_has_match', compact($vars)));
251   
252              if (!$row)
253              {
254                  // The user does not yet exist, ask to link or create profile
255                  return [
256                      'status'        => LOGIN_SUCCESS_LINK_PROFILE,
257                      'error_msg'        => 'LOGIN_OAUTH_ACCOUNT_NOT_LINKED',
258                      'user_row'        => [],
259                      'redirect_data'    => $redirect_data,
260                  ];
261              }
262   
263              // Retrieve the user's account
264              $sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_ip, user_type, user_login_attempts
265                  FROM ' . $this->users_table . '
266                  WHERE user_id = ' . (int) $row['user_id'];
267              $result = $this->db->sql_query($sql);
268              $row = $this->db->sql_fetchrow($result);
269              $this->db->sql_freeresult($result);
270   
271              if (!$row)
272              {
273                  return [
274                      'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
275                      'error_msg'        => 'AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY',
276                      'user_row'        => ['user_id' => ANONYMOUS],
277                  ];
278              }
279   
280              /**
281               * Check if the user is banned.
282               * The fourth parameter (return) has to be true, otherwise the OAuth login is still called and
283               * an uncaught exception is thrown as there is no token stored in the database.
284               */
285              $ban = $this->user->check_ban($row['user_id'], $row['user_ip'], $row['user_email'], true);
286   
287              if (!empty($ban))
288              {
289                  $till_date = !empty($ban['ban_end']) ? $this->user->format_date($ban['ban_end']) : '';
290                  $message = !empty($ban['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
291   
292                  $contact_link = phpbb_get_board_contact_link($this->config, $this->root_path, $this->php_ext);
293   
294                  $message = $this->language->lang($message, $till_date, '<a href="' . $contact_link . '">', '</a>');
295                  $message .= !empty($ban['ban_give_reason']) ? '<br /><br />' . $this->language->lang('BOARD_BAN_REASON', $ban['ban_give_reason']) : '';
296                  $message .= !empty($ban['ban_triggered_by']) ? '<br /><br /><em>' . $this->language->lang('BAN_TRIGGERED_BY_' . utf8_strtoupper($ban['ban_triggered_by'])) . '</em>' : '';
297   
298                  return [
299                      'status'    => LOGIN_BREAK,
300                      'error_msg'    => $message,
301                      'user_row'    => $row,
302                  ];
303              }
304   
305              // Update token storage to store the user_id
306              $storage->set_user_id($row['user_id']);
307   
308              /**
309               * Event is triggered after user is successfully logged in via OAuth.
310               *
311               * @event core.auth_oauth_login_after
312               * @var array    row        User row
313               * @since 3.1.11-RC1
314               */
315              $vars = [
316                  'row',
317              ];
318              extract($this->dispatcher->trigger_event('core.auth_oauth_login_after', compact($vars)));
319   
320              // The user is now authenticated and can be logged in
321              return [
322                  'status'        => LOGIN_SUCCESS,
323                  'error_msg'        => false,
324                  'user_row'        => $row,
325              ];
326          }
327          else
328          {
329              return $this->set_redirect($service);
330          }
331      }
332   
333      /**
334       * {@inheritdoc}
335       */
336      public function get_login_data()
337      {
338          $login_data = [
339              'TEMPLATE_FILE'        => 'login_body_oauth.html',
340              'BLOCK_VAR_NAME'    => 'oauth',
341              'BLOCK_VARS'        => [],
342          ];
343   
344          foreach ($this->service_providers as $service_name => $service_provider)
345          {
346              // Only include data if the credentials are set
347              $credentials = $service_provider->get_service_credentials();
348   
349              if ($credentials['key'] && $credentials['secret'])
350              {
351                  $provider = $this->get_provider($service_name);
352                  $redirect_url = generate_board_url() . '/ucp.' . $this->php_ext . '?mode=login&login=external&oauth_service=' . $provider;
353   
354                  $login_data['BLOCK_VARS'][$service_name] = [
355                      'REDIRECT_URL'    => redirect($redirect_url, true),
356                      'SERVICE_NAME'    => $this->get_provider_title($provider),
357                  ];
358              }
359          }
360   
361          return $login_data;
362      }
363   
364      /**
365       * {@inheritdoc}
366       */
367      public function acp()
368      {
369          $ret = [];
370   
371          foreach ($this->service_providers as $service_name => $service_provider)
372          {
373              $provider = $this->get_provider($service_name);
374   
375              $provider = utf8_strtolower($provider);
376   
377              $ret[] = 'auth_oauth_' . $provider . '_key';
378              $ret[] = 'auth_oauth_' . $provider . '_secret';
379          }
380   
381          return $ret;
382      }
383   
384      /**
385       * {@inheritdoc}
386       */
387      public function get_acp_template($new_config)
388      {
389          $ret = [
390              'BLOCK_VAR_NAME'    => 'oauth_services',
391              'BLOCK_VARS'        => [],
392              'TEMPLATE_FILE'        => 'auth_provider_oauth.html',
393              'TEMPLATE_VARS'        => [],
394          ];
395   
396          foreach ($this->service_providers as $service_name => $service_provider)
397          {
398              $provider = $this->get_provider($service_name);
399   
400              $ret['BLOCK_VARS'][$provider] = [
401                  'NAME'            => $provider,
402                  'ACTUAL_NAME'    => $this->get_provider_title($provider),
403                  'KEY'            => $new_config['auth_oauth_' . utf8_strtolower($provider) . '_key'],
404                  'SECRET'        => $new_config['auth_oauth_' . utf8_strtolower($provider) . '_secret'],
405              ];
406          }
407   
408          return $ret;
409      }
410   
411      /**
412       * {@inheritdoc}
413       */
414      public function login_link_has_necessary_data(array $login_link_data)
415      {
416          if (empty($login_link_data))
417          {
418              return 'LOGIN_LINK_NO_DATA_PROVIDED';
419          }
420   
421          if (!array_key_exists('oauth_service', $login_link_data) || !$login_link_data['oauth_service'] ||
422              !array_key_exists('link_method', $login_link_data) || !$login_link_data['link_method'])
423          {
424              return 'LOGIN_LINK_MISSING_DATA';
425          }
426   
427          return null;
428      }
429   
430      /**
431       * {@inheritdoc}
432       */
433      public function link_account(array $link_data)
434      {
435          // Check for a valid link method (auth_link or login_link)
436          if (!array_key_exists('link_method', $link_data) ||
437              !in_array($link_data['link_method'], ['auth_link', 'login_link']))
438          {
439              return 'LOGIN_LINK_MISSING_DATA';
440          }
441   
442          // We must have an oauth_service listed, check for it two ways
443          if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
444          {
445              $link_data['oauth_service'] = $this->request->variable('oauth_service', '');
446   
447              if (!$link_data['oauth_service'])
448              {
449                  return 'LOGIN_LINK_MISSING_DATA';
450              }
451          }
452   
453          $service_name = $this->get_service_name($link_data['oauth_service']);
454   
455          if (!$this->service_providers->offsetExists($service_name))
456          {
457              return 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST';
458          }
459   
460          switch ($link_data['link_method'])
461          {
462              case 'auth_link':
463                  return $this->link_account_auth_link($link_data, $service_name);
464              case 'login_link':
465                  return $this->link_account_login_link($link_data, $service_name);
466              default:
467                  return 'LOGIN_LINK_MISSING_DATA';
468          }
469      }
470   
471      /**
472       * {@inheritdoc}
473       */
474      public function logout($data, $new_session)
475      {
476          // Clear all tokens belonging to the user
477          $storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
478          $storage->clearAllTokens();
479   
480          return;
481      }
482   
483      /**
484       * {@inheritdoc}
485       */
486      public function get_auth_link_data($user_id = 0)
487      {
488          $user_ids    = [];
489          $block_vars    = [];
490   
491          $sql = 'SELECT oauth_provider_id, provider
492               FROM ' . $this->oauth_account_table . '
493              WHERE user_id = ' . ($user_id > 0 ? (int) $user_id : (int) $this->user->data['user_id']);
494          $result = $this->db->sql_query($sql);
495          while ($row = $this->db->sql_fetchrow($result))
496          {
497              $user_ids[$row['provider']] = $row['oauth_provider_id'];
498          }
499          $this->db->sql_freeresult($result);
500   
501          foreach ($this->service_providers as $service_name => $service_provider)
502          {
503              // Only include data if the credentials are set
504              $credentials = $service_provider->get_service_credentials();
505   
506              if ($credentials['key'] && $credentials['secret'])
507              {
508                  $provider = $this->get_provider($service_name);
509   
510                  $block_vars[$service_name] = [
511                      'SERVICE_NAME'    => $this->get_provider_title($provider),
512                      'UNIQUE_ID'        => isset($user_ids[$provider]) ? $user_ids[$provider] : null,
513                      'HIDDEN_FIELDS'    => [
514                          'link'            => !isset($user_ids[$provider]),
515                          'oauth_service' => $provider,
516                      ],
517                  ];
518              }
519          }
520   
521          return [
522              'BLOCK_VAR_NAME'    => 'oauth',
523              'BLOCK_VARS'        => $block_vars,
524   
525              'TEMPLATE_FILE'        => 'ucp_auth_link_oauth.html',
526          ];
527      }
528   
529      /**
530       * {@inheritdoc}
531       */
532      public function unlink_account(array $link_data)
533      {
534          if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
535          {
536              return 'LOGIN_LINK_MISSING_DATA';
537          }
538   
539          // Remove user specified in $link_data if possible
540          $user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id'];
541   
542          // Remove the link
543          $sql = 'DELETE FROM ' . $this->oauth_account_table . "
544              WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "'
545                  AND user_id = " . (int) $user_id;
546          $this->db->sql_query($sql);
547   
548          $service_name = $this->get_service_name($link_data['oauth_service']);
549   
550          // Clear all tokens belonging to the user on this service
551          $storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
552          $storage->clearToken($service_name);
553   
554          return false;
555      }
556   
557      /**
558       * Performs the account linking for login_link.
559       *
560       * @param array        $link_data        The same variable given to
561       *                                     {@see \phpbb\auth\provider\provider_interface::link_account}
562       * @param string    $service_name    The name of the service being used in linking.
563       * @return string|false                Returns a language key (string) if an error is encountered,
564       *                                     or false on success.
565       */
566      protected function link_account_login_link(array $link_data, $service_name)
567      {
568          $storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
569   
570          // Check for an access token, they should have one
571          if (!$storage->has_access_token_by_session($service_name))
572          {
573              return 'LOGIN_LINK_ERROR_OAUTH_NO_ACCESS_TOKEN';
574          }
575   
576          // Prepare for an authentication request
577          $query = 'mode=login_link&login_link_oauth_service=' . $link_data['oauth_service'];
578   
579          try
580          {
581              $service = $this->get_service($link_data['oauth_service'], $storage, $query);
582          }
583          catch (\Exception $e)
584          {
585              return $e->getMessage();
586          }
587   
588          $this->service_providers[$service_name]->set_external_service_provider($service);
589   
590          try
591          {
592              // The user has already authenticated successfully, request to authenticate again
593              $unique_id = $this->service_providers[$service_name]->perform_token_auth();
594          }
595          catch (exception $e)
596          {
597              return $e->getMessage();
598          }
599   
600          // Insert into table, they will be able to log in after this
601          $data = [
602              'user_id'            => $link_data['user_id'],
603              'provider'            => utf8_strtolower($link_data['oauth_service']),
604              'oauth_provider_id'    => $unique_id,
605          ];
606   
607          $this->link_account_perform_link($data);
608   
609          // Update token storage to store the user_id
610          $storage->set_user_id($link_data['user_id']);
611   
612          return false;
613      }
614   
615      /**
616       * Performs the account linking for auth_link.
617       *
618       * @param array        $link_data        The same variable given to
619       *                                     {@see \phpbb\auth\provider\provider_interface::link_account}
620       * @param string    $service_name    The name of the service being used in linking.
621       * @return string|false                Returns a language constant (string) if an error is encountered,
622       *                                     or false on success.
623       */
624      protected function link_account_auth_link(array $link_data, $service_name)
625      {
626          $storage = new token_storage($this->db, $this->user, $this->oauth_token_table, $this->oauth_state_table);
627          $query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . $link_data['oauth_service'];
628   
629          try
630          {
631              /** @var OAuth1Service|OAuth2Service $service */
632              $service = $this->get_service($link_data['oauth_service'], $storage, $query);
633          }
634          catch (\Exception $e)
635          {
636              return $e->getMessage();
637          }
638   
639          if ($this->is_set_code($service))
640          {
641              $this->service_providers[$service_name]->set_external_service_provider($service);
642   
643              try
644              {
645                  $unique_id = $this->service_providers[$service_name]->perform_auth_login();
646              }
647              catch (exception $e)
648              {
649                  return $e->getMessage();
650              }
651   
652              // Insert into table, they will be able to log in after this
653              $data = [
654                  'user_id'            => $this->user->data['user_id'],
655                  'provider'            => utf8_strtolower($link_data['oauth_service']),
656                  'oauth_provider_id'    => $unique_id,
657              ];
658   
659              $this->link_account_perform_link($data);
660   
661              return false;
662          }
663          else
664          {
665              return $this->set_redirect($service);
666          }
667      }
668   
669      /**
670       * Performs the query that inserts an account link
671       *
672       * @param    array    $data    This array is passed to db->sql_build_array
673       */
674      protected function link_account_perform_link(array $data)
675      {
676          // Check if the external account is already associated with other user
677          $sql = 'SELECT user_id
678              FROM ' . $this->oauth_account_table . "
679              WHERE provider = '" . $this->db->sql_escape($data['provider']) . "'
680                  AND oauth_provider_id = '" . $this->db->sql_escape($data['oauth_provider_id']) . "'";
681          $result = $this->db->sql_query($sql);
682          $row = $this->db->sql_fetchrow($result);
683          $this->db->sql_freeresult($result);
684   
685          if ($row)
686          {
687              trigger_error('AUTH_PROVIDER_OAUTH_ERROR_ALREADY_LINKED');
688          }
689   
690          // Link account
691          $sql = 'INSERT INTO ' . $this->oauth_account_table . ' ' . $this->db->sql_build_array('INSERT', $data);
692          $this->db->sql_query($sql);
693   
694          /**
695           * Event is triggered after user links account.
696           *
697           * @event core.auth_oauth_link_after
698           * @var array    data    User row
699           * @since 3.1.11-RC1
700           */
701          $vars = [
702              'data',
703          ];
704          extract($this->dispatcher->trigger_event('core.auth_oauth_link_after', compact($vars)));
705      }
706   
707      /**
708       * Returns a new service object.
709       *
710       * @param string            $provider        The name of the provider
711       * @param token_storage        $storage        Token storage object
712       * @param string            $query            The query string used for the redirect uri
713       * @return ServiceInterface
714       * @throws exception                        When OAuth service was not created
715       */
716      protected function get_service($provider, token_storage $storage, $query)
717      {
718          $service_name = $this->get_service_name($provider);
719   
720          /** @see \phpbb\auth\provider\oauth\service\service_interface::get_service_credentials */
721          $service_credentials = $this->service_providers[$service_name]->get_service_credentials();
722   
723          /** @see \phpbb\auth\provider\oauth\service\service_interface::get_auth_scope */
724          $scopes = $this->service_providers[$service_name]->get_auth_scope();
725   
726          $callback = generate_board_url() . "/ucp.{$this->php_ext}?{$query}";
727   
728          // Setup the credentials for the requests
729          $credentials = new Credentials(
730              $service_credentials['key'],
731              $service_credentials['secret'],
732              $callback
733          );
734   
735          $service_factory = new ServiceFactory;
736   
737          // Allow providers to register a custom class or override the provider name
738          if ($class = $this->service_providers[$service_name]->get_external_service_class())
739          {
740              if (class_exists($class))
741              {
742                  try
743                  {
744                      $service_factory->registerService($provider, $class);
745                  }
746                  catch (\OAuth\Common\Exception\Exception $e)
747                  {
748                      throw new exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_SERVICE_TYPE');
749                  }
750              }
751              else
752              {
753                  $provider = $class;
754              }
755          }
756   
757          $service = $service_factory->createService($provider, $credentials, $storage, $scopes);
758   
759          if (!$service)
760          {
761              throw new exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');
762          }
763   
764          return $service;
765      }
766   
767      /**
768       * Returns the service name for an OAuth provider name.
769       *
770       * @param string    $provider        The OAuth provider name
771       * @return string                    The service name
772       */
773      protected function get_service_name($provider)
774      {
775          if (strpos($provider, 'auth.provider.oauth.service.') !== 0)
776          {
777              $provider = 'auth.provider.oauth.service.' . utf8_strtolower($provider);
778          }
779   
780          return $provider;
781      }
782   
783      /**
784       * Returns the OAuth provider name from a service name.
785       *
786       * @param string    $service_name    The service name
787       * @return string                    The OAuth provider name
788       */
789      protected function get_provider($service_name)
790      {
791          return str_replace('auth.provider.oauth.service.', '', $service_name);
792      }
793   
794      /**
795       * Returns the localized title for the OAuth provider.
796       *
797       * @param string    $provider        The OAuth provider name
798       * @return string                    The OAuth provider title
799       */
800      protected function get_provider_title($provider)
801      {
802          return $this->language->lang('AUTH_PROVIDER_OAUTH_SERVICE_' . utf8_strtoupper($provider));
803      }
804   
805      /**
806       * Returns whether or not the authorization code is set.
807       *
808       * @param OAuth1Service|OAuth2Service    $service    The external OAuth service
809       * @return bool                                        Whether or not the authorization code is set in the URL
810       *                                                   for the respective OAuth service's version
811       */
812      protected function is_set_code($service)
813      {
814          switch ($service::OAUTH_VERSION)
815          {
816              case 1:
817                  return $this->request->is_set('oauth_token', request_interface::GET);
818   
819              case 2:
820                  return $this->request->is_set('code', request_interface::GET);
821   
822              default:
823                  return false;
824          }
825      }
826   
827      /**
828       * Sets a redirect to the authorization uri.
829       *
830       * @param OAuth1Service|OAuth2Service $service        The external OAuth service
831       * @return array|false                                Array if an error occurred,
832       *                                                    false on success
833       */
834      protected function set_redirect($service)
835      {
836          $parameters = [];
837   
838          if ($service::OAUTH_VERSION === 1)
839          {
840              try
841              {
842                  $token        = $service->requestRequestToken();
843                  $parameters    = ['oauth_token' => $token->getRequestToken()];
844              }
845              catch (TokenResponseException $e)
846              {
847                  return [
848                      'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
849                      'error_msg'        => $e->getMessage(),
850                      'user_row'        => ['user_id' => ANONYMOUS],
851                  ];
852              }
853          }
854   
855          redirect($service->getAuthorizationUri($parameters), false, true);
856   
857          return false;
858      }
859  }
860