Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

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: 09.10.2024, 12:55 - Dateigröße: 21.25 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\Consumer\Credentials;
017   
018  /**
019  * OAuth authentication provider for phpBB3
020  */
021  class oauth extends \phpbb\auth\provider\base
022  {
023      /**
024      * Database driver
025      *
026      * @var \phpbb\db\driver\driver_interface
027      */
028      protected $db;
029   
030      /**
031      * phpBB config
032      *
033      * @var \phpbb\config\config
034      */
035      protected $config;
036   
037      /**
038      * phpBB passwords manager
039      *
040      * @var \phpbb\passwords\manager
041      */
042      protected $passwords_manager;
043   
044      /**
045      * phpBB request object
046      *
047      * @var \phpbb\request\request_interface
048      */
049      protected $request;
050   
051      /**
052      * phpBB user
053      *
054      * @var \phpbb\user
055      */
056      protected $user;
057   
058      /**
059      * OAuth token table
060      *
061      * @var string
062      */
063      protected $auth_provider_oauth_token_storage_table;
064   
065      /**
066      * OAuth state table
067      *
068      * @var string
069      */
070      protected $auth_provider_oauth_state_table;
071   
072      /**
073      * OAuth account association table
074      *
075      * @var string
076      */
077      protected $auth_provider_oauth_token_account_assoc;
078   
079      /**
080      * All OAuth service providers
081      *
082      * @var \phpbb\di\service_collection Contains \phpbb\auth\provider\oauth\service_interface
083      */
084      protected $service_providers;
085   
086      /**
087      * Users table
088      *
089      * @var string
090      */
091      protected $users_table;
092   
093      /**
094      * Cached current uri object
095      *
096      * @var \OAuth\Common\Http\Uri\UriInterface|null
097      */
098      protected $current_uri;
099   
100      /**
101      * DI container
102      *
103      * @var \Symfony\Component\DependencyInjection\ContainerInterface
104      */
105      protected $phpbb_container;
106   
107      /**
108      * phpBB event dispatcher
109      *
110      * @var \phpbb\event\dispatcher_interface
111      */
112      protected $dispatcher;
113   
114      /**
115      * phpBB root path
116      *
117      * @var string
118      */
119      protected $phpbb_root_path;
120   
121      /**
122      * PHP file extension
123      *
124      * @var string
125      */
126      protected $php_ext;
127   
128      /**
129      * OAuth Authentication Constructor
130      *
131      * @param    \phpbb\db\driver\driver_interface    $db
132      * @param    \phpbb\config\config    $config
133      * @param    \phpbb\passwords\manager    $passwords_manager
134      * @param    \phpbb\request\request_interface    $request
135      * @param    \phpbb\user        $user
136      * @param    string            $auth_provider_oauth_token_storage_table
137      * @param    string            $auth_provider_oauth_state_table
138      * @param    string            $auth_provider_oauth_token_account_assoc
139      * @param    \phpbb\di\service_collection    $service_providers Contains \phpbb\auth\provider\oauth\service_interface
140      * @param    string            $users_table
141      * @param    \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container DI container
142      * @param    \phpbb\event\dispatcher_interface $dispatcher phpBB event dispatcher
143      * @param    string            $phpbb_root_path
144      * @param    string            $php_ext
145      */
146      public function __construct(\phpbb\db\driver\driver_interface $db, \phpbb\config\config $config, \phpbb\passwords\manager $passwords_manager, \phpbb\request\request_interface $request, \phpbb\user $user, $auth_provider_oauth_token_storage_table, $auth_provider_oauth_state_table, $auth_provider_oauth_token_account_assoc, \phpbb\di\service_collection $service_providers, $users_table, \Symfony\Component\DependencyInjection\ContainerInterface $phpbb_container, \phpbb\event\dispatcher_interface $dispatcher, $phpbb_root_path, $php_ext)
147      {
148          $this->db = $db;
149          $this->config = $config;
150          $this->passwords_manager = $passwords_manager;
151          $this->request = $request;
152          $this->user = $user;
153          $this->auth_provider_oauth_token_storage_table = $auth_provider_oauth_token_storage_table;
154          $this->auth_provider_oauth_state_table = $auth_provider_oauth_state_table;
155          $this->auth_provider_oauth_token_account_assoc = $auth_provider_oauth_token_account_assoc;
156          $this->service_providers = $service_providers;
157          $this->users_table = $users_table;
158          $this->phpbb_container = $phpbb_container;
159          $this->dispatcher = $dispatcher;
160          $this->phpbb_root_path = $phpbb_root_path;
161          $this->php_ext = $php_ext;
162      }
163   
164      /**
165      * {@inheritdoc}
166      */
167      public function init()
168      {
169          // This does not test whether or not the key and secret provided are valid.
170          foreach ($this->service_providers as $service_provider)
171          {
172              $credentials = $service_provider->get_service_credentials();
173   
174              if (($credentials['key'] && !$credentials['secret']) || (!$credentials['key'] && $credentials['secret']))
175              {
176                  return $this->user->lang['AUTH_PROVIDER_OAUTH_ERROR_ELEMENT_MISSING'];
177              }
178          }
179          return false;
180      }
181   
182      /**
183      * {@inheritdoc}
184      */
185      public function login($username, $password)
186      {
187          // Temporary workaround for only having one authentication provider available
188          if (!$this->request->is_set('oauth_service'))
189          {
190              $provider = new \phpbb\auth\provider\db($this->db, $this->config, $this->passwords_manager, $this->request, $this->user, $this->phpbb_container, $this->phpbb_root_path, $this->php_ext);
191              return $provider->login($username, $password);
192          }
193   
194          // Requst the name of the OAuth service
195          $service_name_original = $this->request->variable('oauth_service', '', false);
196          $service_name = 'auth.provider.oauth.service.' . strtolower($service_name_original);
197          if ($service_name_original === '' || !array_key_exists($service_name, $this->service_providers))
198          {
199              return array(
200                  'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
201                  'error_msg'        => 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST',
202                  'user_row'        => array('user_id' => ANONYMOUS),
203              );
204          }
205   
206          // Get the service credentials for the given service
207          $service_credentials = $this->service_providers[$service_name]->get_service_credentials();
208   
209          $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
210          $query = 'mode=login&login=external&oauth_service=' . $service_name_original;
211          $service = $this->get_service($service_name_original, $storage, $service_credentials, $query, $this->service_providers[$service_name]->get_auth_scope());
212   
213          if (($service::OAUTH_VERSION === 2 && $this->request->is_set('code', \phpbb\request\request_interface::GET))
214              || ($service::OAUTH_VERSION === 1 && $this->request->is_set('oauth_token', \phpbb\request\request_interface::GET)))
215          {
216              $this->service_providers[$service_name]->set_external_service_provider($service);
217              $unique_id = $this->service_providers[$service_name]->perform_auth_login();
218   
219              // Check to see if this provider is already assosciated with an account
220              $data = array(
221                  'provider'    => $service_name_original,
222                  'oauth_provider_id'    => $unique_id
223              );
224              $sql = 'SELECT user_id FROM ' . $this->auth_provider_oauth_token_account_assoc . '
225                  WHERE ' . $this->db->sql_build_array('SELECT', $data);
226              $result = $this->db->sql_query($sql);
227              $row = $this->db->sql_fetchrow($result);
228              $this->db->sql_freeresult($result);
229   
230              if (!$row)
231              {
232                  // The user does not yet exist, ask to link or create profile
233                  return array(
234                      'status'        => LOGIN_SUCCESS_LINK_PROFILE,
235                      'error_msg'        => 'LOGIN_OAUTH_ACCOUNT_NOT_LINKED',
236                      'user_row'        => array(),
237                      'redirect_data'    => array(
238                          'auth_provider'                => 'oauth',
239                          'login_link_oauth_service'    => $service_name_original,
240                      ),
241                  );
242              }
243   
244              // Retrieve the user's account
245              $sql = 'SELECT user_id, username, user_password, user_passchg, user_email, user_type, user_login_attempts
246                  FROM ' . $this->users_table . '
247                      WHERE user_id = ' . (int) $row['user_id'];
248              $result = $this->db->sql_query($sql);
249              $row = $this->db->sql_fetchrow($result);
250              $this->db->sql_freeresult($result);
251   
252              if (!$row)
253              {
254                  throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_INVALID_ENTRY');
255              }
256   
257              // Update token storage to store the user_id
258              $storage->set_user_id($row['user_id']);
259   
260              /**
261              * Event is triggered after user is successfuly logged in via OAuth.
262              *
263              * @event core.auth_oauth_login_after
264              * @var    array    row    User row
265              * @since 3.1.11-RC1
266              */
267              $vars = array(
268                  'row',
269              );
270              extract($this->dispatcher->trigger_event('core.auth_oauth_login_after', compact($vars)));
271   
272              // The user is now authenticated and can be logged in
273              return array(
274                  'status'        => LOGIN_SUCCESS,
275                  'error_msg'        => false,
276                  'user_row'        => $row,
277              );
278          }
279          else
280          {
281              if ($service::OAUTH_VERSION === 1)
282              {
283                  $token = $service->requestRequestToken();
284                  $url = $service->getAuthorizationUri(array('oauth_token' => $token->getRequestToken()));
285              }
286              else
287              {
288                  $url = $service->getAuthorizationUri();
289              }
290              header('Location: ' . $url);
291          }
292      }
293   
294      /**
295      * Returns the cached current_uri object or creates and caches it if it is
296      * not already created. In each case the query string is updated based on
297      * the $query parameter.
298      *
299      * @param    string    $service_name    The name of the service
300      * @param    string    $query            The query string of the current_uri
301      *                                    used in redirects
302      * @return    \OAuth\Common\Http\Uri\UriInterface
303      */
304      protected function get_current_uri($service_name, $query)
305      {
306          if ($this->current_uri)
307          {
308              $this->current_uri->setQuery($query);
309              return $this->current_uri;
310          }
311   
312          $uri_factory = new \OAuth\Common\Http\Uri\UriFactory();
313          $super_globals = $this->request->get_super_global(\phpbb\request\request_interface::SERVER);
314          if (!empty($super_globals['HTTP_X_FORWARDED_PROTO']) && $super_globals['HTTP_X_FORWARDED_PROTO'] === 'https')
315          {
316              $super_globals['HTTPS'] = 'on';
317              $super_globals['SERVER_PORT'] = 443;
318          }
319          $current_uri = $uri_factory->createFromSuperGlobalArray($super_globals);
320          $current_uri->setQuery($query);
321   
322          $this->current_uri = $current_uri;
323          return $current_uri;
324      }
325   
326      /**
327      * Returns a new service object
328      *
329      * @param    string    $service_name            The name of the service
330      * @param    \phpbb\auth\provider\oauth\token_storage $storage
331      * @param    array    $service_credentials    {@see \phpbb\auth\provider\oauth\oauth::get_service_credentials}
332      * @param    string    $query                    The query string of the
333      *                                            current_uri used in redirection
334      * @param    array    $scopes                    The scope of the request against
335      *                                            the api.
336      * @return    \OAuth\Common\Service\ServiceInterface
337      * @throws    \Exception
338      */
339      protected function get_service($service_name, \phpbb\auth\provider\oauth\token_storage $storage, array $service_credentials, $query, array $scopes = array())
340      {
341          $current_uri = $this->get_current_uri($service_name, $query);
342   
343          // Setup the credentials for the requests
344          $credentials = new Credentials(
345              $service_credentials['key'],
346              $service_credentials['secret'],
347              $current_uri->getAbsoluteUri()
348          );
349   
350          $service_factory = new \OAuth\ServiceFactory();
351          $service = $service_factory->createService($service_name, $credentials, $storage, $scopes);
352   
353          if (!$service)
354          {
355              throw new \Exception('AUTH_PROVIDER_OAUTH_ERROR_SERVICE_NOT_CREATED');
356          }
357   
358          return $service;
359      }
360   
361      /**
362      * {@inheritdoc}
363      */
364      public function get_login_data()
365      {
366          $login_data = array(
367              'TEMPLATE_FILE'        => 'login_body_oauth.html',
368              'BLOCK_VAR_NAME'    => 'oauth',
369              'BLOCK_VARS'        => array(),
370          );
371   
372          foreach ($this->service_providers as $service_name => $service_provider)
373          {
374              // Only include data if the credentials are set
375              $credentials = $service_provider->get_service_credentials();
376              if ($credentials['key'] && $credentials['secret'])
377              {
378                  $actual_name = str_replace('auth.provider.oauth.service.', '', $service_name);
379                  $redirect_url = build_url(false) . '&login=external&oauth_service=' . $actual_name;
380                  $login_data['BLOCK_VARS'][$service_name] = array(
381                      'REDIRECT_URL'    => redirect($redirect_url, true),
382                      'SERVICE_NAME'    => $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)],
383                  );
384              }
385          }
386   
387          return $login_data;
388      }
389   
390      /**
391      * {@inheritdoc}
392      */
393      public function acp()
394      {
395          $ret = array();
396   
397          foreach ($this->service_providers as $service_name => $service_provider)
398          {
399              $actual_name = str_replace('auth.provider.oauth.service.', '', $service_name);
400              $ret[] = 'auth_oauth_' . $actual_name . '_key';
401              $ret[] = 'auth_oauth_' . $actual_name . '_secret';
402          }
403   
404          return $ret;
405      }
406   
407      /**
408      * {@inheritdoc}
409      */
410      public function get_acp_template($new_config)
411      {
412          $ret = array(
413              'BLOCK_VAR_NAME'    => 'oauth_services',
414              'BLOCK_VARS'        => array(),
415              'TEMPLATE_FILE'        => 'auth_provider_oauth.html',
416              'TEMPLATE_VARS'        => array(),
417          );
418   
419          foreach ($this->service_providers as $service_name => $service_provider)
420          {
421              $actual_name = str_replace('auth.provider.oauth.service.', '', $service_name);
422              $ret['BLOCK_VARS'][$actual_name] = array(
423                  'ACTUAL_NAME'    => $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)],
424                  'KEY'            => $new_config['auth_oauth_' . $actual_name . '_key'],
425                  'NAME'            => $actual_name,
426                  'SECRET'        => $new_config['auth_oauth_' . $actual_name . '_secret'],
427              );
428          }
429   
430          return $ret;
431      }
432   
433      /**
434      * {@inheritdoc}
435      */
436      public function login_link_has_necessary_data($login_link_data)
437      {
438          if (empty($login_link_data))
439          {
440              return 'LOGIN_LINK_NO_DATA_PROVIDED';
441          }
442   
443          if (!array_key_exists('oauth_service', $login_link_data) || !$login_link_data['oauth_service'] ||
444              !array_key_exists('link_method', $login_link_data) || !$login_link_data['link_method'])
445          {
446              return 'LOGIN_LINK_MISSING_DATA';
447          }
448   
449          return null;
450      }
451   
452      /**
453      * {@inheritdoc}
454      */
455      public function link_account(array $link_data)
456      {
457          // Check for a valid link method (auth_link or login_link)
458          if (!array_key_exists('link_method', $link_data) ||
459              !in_array($link_data['link_method'], array(
460                  'auth_link',
461                  'login_link',
462              )))
463          {
464              return 'LOGIN_LINK_MISSING_DATA';
465          }
466   
467          // We must have an oauth_service listed, check for it two ways
468          if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
469          {
470              $link_data['oauth_service'] = $this->request->variable('oauth_service', '');
471   
472              if (!$link_data['oauth_service'])
473              {
474                  return 'LOGIN_LINK_MISSING_DATA';
475              }
476          }
477   
478          $service_name = 'auth.provider.oauth.service.' . strtolower($link_data['oauth_service']);
479          if (!array_key_exists($service_name, $this->service_providers))
480          {
481              return 'LOGIN_ERROR_OAUTH_SERVICE_DOES_NOT_EXIST';
482          }
483   
484          switch ($link_data['link_method'])
485          {
486              case 'auth_link':
487                  return $this->link_account_auth_link($link_data, $service_name);
488              case 'login_link':
489                  return $this->link_account_login_link($link_data, $service_name);
490          }
491      }
492   
493      /**
494      * Performs the account linking for login_link
495      *
496      * @param    array    $link_data        The same variable given to {@see \phpbb\auth\provider\provider_interface::link_account}
497      * @param    string    $service_name    The name of the service being used in
498      *                                    linking.
499      * @return    string|null    Returns a language constant (string) if an error is
500      *                        encountered, or null on success.
501      */
502      protected function link_account_login_link(array $link_data, $service_name)
503      {
504          $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
505   
506          // Check for an access token, they should have one
507          if (!$storage->has_access_token_by_session($service_name))
508          {
509              return 'LOGIN_LINK_ERROR_OAUTH_NO_ACCESS_TOKEN';
510          }
511   
512          // Prepare the query string
513          $query = 'mode=login_link&login_link_oauth_service=' . strtolower($link_data['oauth_service']);
514   
515          // Prepare for an authentication request
516          $service_credentials = $this->service_providers[$service_name]->get_service_credentials();
517          $scopes = $this->service_providers[$service_name]->get_auth_scope();
518          $service = $this->get_service(strtolower($link_data['oauth_service']), $storage, $service_credentials, $query, $scopes);
519          $this->service_providers[$service_name]->set_external_service_provider($service);
520   
521          // The user has already authenticated successfully, request to authenticate again
522          $unique_id = $this->service_providers[$service_name]->perform_token_auth();
523   
524          // Insert into table, they will be able to log in after this
525          $data = array(
526              'user_id'            => $link_data['user_id'],
527              'provider'            => strtolower($link_data['oauth_service']),
528              'oauth_provider_id'    => $unique_id,
529          );
530   
531          $this->link_account_perform_link($data);
532          // Update token storage to store the user_id
533          $storage->set_user_id($link_data['user_id']);
534      }
535   
536      /**
537      * Performs the account linking for auth_link
538      *
539      * @param    array    $link_data        The same variable given to {@see \phpbb\auth\provider\provider_interface::link_account}
540      * @param    string    $service_name    The name of the service being used in
541      *                                    linking.
542      * @return    string|null    Returns a language constant (string) if an error is
543      *                        encountered, or null on success.
544      */
545      protected function link_account_auth_link(array $link_data, $service_name)
546      {
547          $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
548          $query = 'i=ucp_auth_link&mode=auth_link&link=1&oauth_service=' . strtolower($link_data['oauth_service']);
549          $service_credentials = $this->service_providers[$service_name]->get_service_credentials();
550          $scopes = $this->service_providers[$service_name]->get_auth_scope();
551          $service = $this->get_service(strtolower($link_data['oauth_service']), $storage, $service_credentials, $query, $scopes);
552   
553          if (($service::OAUTH_VERSION === 2 && $this->request->is_set('code', \phpbb\request\request_interface::GET))
554              || ($service::OAUTH_VERSION === 1 && $this->request->is_set('oauth_token', \phpbb\request\request_interface::GET)))
555          {
556              $this->service_providers[$service_name]->set_external_service_provider($service);
557              $unique_id = $this->service_providers[$service_name]->perform_auth_login();
558   
559              // Insert into table, they will be able to log in after this
560              $data = array(
561                  'user_id'            => $this->user->data['user_id'],
562                  'provider'            => strtolower($link_data['oauth_service']),
563                  'oauth_provider_id'    => $unique_id,
564              );
565   
566              $this->link_account_perform_link($data);
567          }
568          else
569          {
570              if ($service::OAUTH_VERSION === 1)
571              {
572                  $token = $service->requestRequestToken();
573                  $url = $service->getAuthorizationUri(array('oauth_token' => $token->getRequestToken()));
574              }
575              else
576              {
577                  $url = $service->getAuthorizationUri();
578              }
579              header('Location: ' . $url);
580          }
581      }
582   
583      /**
584      * Performs the query that inserts an account link
585      *
586      * @param    array    $data    This array is passed to db->sql_build_array
587      */
588      protected function link_account_perform_link(array $data)
589      {
590          $sql = 'INSERT INTO ' . $this->auth_provider_oauth_token_account_assoc . '
591              ' . $this->db->sql_build_array('INSERT', $data);
592          $this->db->sql_query($sql);
593   
594          /**
595           * Event is triggered after user links account.
596           *
597           * @event core.auth_oauth_link_after
598           * @var    array    data    User row
599           * @since 3.1.11-RC1
600           */
601          $vars = array(
602              'data',
603          );
604          extract($this->dispatcher->trigger_event('core.auth_oauth_link_after', compact($vars)));
605      }
606   
607      /**
608      * {@inheritdoc}
609      */
610      public function logout($data, $new_session)
611      {
612          // Clear all tokens belonging to the user
613          $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
614          $storage->clearAllTokens();
615   
616          return;
617      }
618   
619      /**
620      * {@inheritdoc}
621      */
622      public function get_auth_link_data($user_id = 0)
623      {
624          $block_vars = array();
625   
626          // Get all external accounts tied to the current user
627          $data = array(
628              'user_id' => ($user_id <= 0) ? (int) $this->user->data['user_id'] : (int) $user_id,
629          );
630          $sql = 'SELECT oauth_provider_id, provider FROM ' . $this->auth_provider_oauth_token_account_assoc . '
631              WHERE ' . $this->db->sql_build_array('SELECT', $data);
632          $result = $this->db->sql_query($sql);
633          $rows = $this->db->sql_fetchrowset($result);
634          $this->db->sql_freeresult($result);
635   
636          $oauth_user_ids = array();
637   
638          if ($rows !== false && sizeof($rows))
639          {
640              foreach ($rows as $row)
641              {
642                  $oauth_user_ids[$row['provider']] = $row['oauth_provider_id'];
643              }
644          }
645          unset($rows);
646   
647          foreach ($this->service_providers as $service_name => $service_provider)
648          {
649              // Only include data if the credentials are set
650              $credentials = $service_provider->get_service_credentials();
651              if ($credentials['key'] && $credentials['secret'])
652              {
653                  $actual_name = str_replace('auth.provider.oauth.service.', '', $service_name);
654   
655                  $block_vars[$service_name] = array(
656                      'HIDDEN_FIELDS'    => array(
657                          'link'            => (!isset($oauth_user_ids[$actual_name])),
658                          'oauth_service' => $actual_name,
659                      ),
660   
661                      'SERVICE_NAME'    => $this->user->lang['AUTH_PROVIDER_OAUTH_SERVICE_' . strtoupper($actual_name)],
662                      'UNIQUE_ID'        => (isset($oauth_user_ids[$actual_name])) ? $oauth_user_ids[$actual_name] : null,
663                  );
664              }
665          }
666   
667          return array(
668              'BLOCK_VAR_NAME'    => 'oauth',
669              'BLOCK_VARS'        => $block_vars,
670   
671              'TEMPLATE_FILE'    => 'ucp_auth_link_oauth.html',
672          );
673      }
674   
675      /**
676      * {@inheritdoc}
677      */
678      public function unlink_account(array $link_data)
679      {
680          if (!array_key_exists('oauth_service', $link_data) || !$link_data['oauth_service'])
681          {
682              return 'LOGIN_LINK_MISSING_DATA';
683          }
684   
685          // Remove user specified in $link_data if possible
686          $user_id = isset($link_data['user_id']) ? $link_data['user_id'] : $this->user->data['user_id'];
687   
688          // Remove the link
689          $sql = 'DELETE FROM ' . $this->auth_provider_oauth_token_account_assoc . "
690              WHERE provider = '" . $this->db->sql_escape($link_data['oauth_service']) . "'
691                  AND user_id = " . (int) $user_id;
692          $this->db->sql_query($sql);
693   
694          // Clear all tokens belonging to the user on this servce
695          $service_name = 'auth.provider.oauth.service.' . strtolower($link_data['oauth_service']);
696          $storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->auth_provider_oauth_token_storage_table, $this->auth_provider_oauth_state_table);
697          $storage->clearToken($service_name);
698      }
699  }
700