friendship ended with social-app. php is my new best friend
1<?php 2/** 3 * Class Deezer 4 * 5 * @created 09.08.2018 6 * @author Smiley <smiley@chillerlan.net> 7 * @copyright 2018 Smiley 8 * @license MIT 9 * 10 * @noinspection PhpUnused 11 */ 12declare(strict_types=1); 13 14namespace chillerlan\OAuth\Providers; 15 16use chillerlan\HTTP\Utils\{MessageUtil, QueryUtil}; 17use chillerlan\OAuth\Core\{AuthenticatedUser, CSRFToken, InvalidAccessTokenException, OAuth2Provider, UserInfo}; 18use Psr\Http\Message\ResponseInterface; 19use function array_merge, implode, trim; 20 21/** 22 * Deezer OAuth2 23 * 24 * @link https://developers.deezer.com/api/oauth 25 */ 26class Deezer extends OAuth2Provider implements CSRFToken, UserInfo{ 27 28 public const IDENTIFIER = 'DEEZER'; 29 30 public const SCOPE_BASIC = 'basic_access'; 31 public const SCOPE_EMAIL = 'email'; 32 public const SCOPE_OFFLINE_ACCESS = 'offline_access'; 33 public const SCOPE_MANAGE_LIBRARY = 'manage_library'; 34 public const SCOPE_MANAGE_COMMUNITY = 'manage_community'; 35 public const SCOPE_DELETE_LIBRARY = 'delete_library'; 36 public const SCOPE_LISTENING_HISTORY = 'listening_history'; 37 38 public const DEFAULT_SCOPES = [ 39 self::SCOPE_BASIC, 40 self::SCOPE_EMAIL, 41 self::SCOPE_OFFLINE_ACCESS, 42 self::SCOPE_MANAGE_LIBRARY, 43 self::SCOPE_LISTENING_HISTORY, 44 ]; 45 46 public const AUTH_METHOD = self::AUTH_METHOD_QUERY; 47 48 protected string $authorizationURL = 'https://connect.deezer.com/oauth/auth.php'; 49 protected string $accessTokenURL = 'https://connect.deezer.com/oauth/access_token.php'; 50 protected string $apiURL = 'https://api.deezer.com'; 51 protected string|null $userRevokeURL = 'https://www.deezer.com/account/apps'; 52 protected string|null $apiDocs = 'https://developers.deezer.com/api'; 53 protected string|null $applicationURL = 'https://developers.deezer.com/myapps'; 54 55 /** 56 * @inheritDoc 57 * 58 * sure, you *can* use different parameter names than the standard ones... https://xkcd.com/927/ 59 */ 60 protected function getAuthorizationURLRequestParams(array $params, array $scopes):array{ 61 62 $params = array_merge($params, [ 63 'app_id' => $this->options->key, 64 'redirect_uri' => $this->options->callbackURL, 65 'perms' => implode($this::SCOPES_DELIMITER, $scopes), 66 ]); 67 68 return $this->setState($params); // we are instance of CSRFToken 69 } 70 71 /** 72 * @inheritDoc 73 */ 74 protected function getAccessTokenRequestBodyParams(string $code):array{ 75 return [ 76 'app_id' => $this->options->key, 77 'secret' => $this->options->secret, 78 'code' => $code, 79 'output' => 'json', // for some reason this has no effect 80 ]; 81 } 82 83 /** 84 * @inheritDoc 85 * 86 * hey deezer, I suggest re-reading the OAuth2 spec! 87 * also the content-type of "text/html" here is... bad. 88 */ 89 protected function getTokenResponseData(ResponseInterface $response):array{ 90 $data = trim(MessageUtil::getContents($response)); 91 92 if($data === ''){ 93 throw new ProviderException('invalid response'); 94 } 95 96 return QueryUtil::parse($data); 97 } 98 99 /** 100 * deezer keeps testing my sanity - HTTP/200 on invalid token... sure 101 * 102 * @inheritDoc 103 * @codeCoverageIgnore 104 */ 105 public function me():AuthenticatedUser{ 106 $json = $this->getMeResponseData('/user/me'); 107 108 if(isset($json['error']['code'], $json['error']['message'])){ 109 110 if($json['error']['code'] === 300){ 111 throw new InvalidAccessTokenException($json['error']['message']); 112 } 113 114 throw new ProviderException($json['error']['message']); 115 } 116 117 $userdata = [ 118 'data' => $json, 119 'avatar' => $json['picture'], 120 'handle' => $json['name'], 121 'id' => $json['id'], 122 'url' => $json['link'], 123 ]; 124 125 return new AuthenticatedUser($userdata); 126 } 127 128}