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}