Twitch   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 7
eloc 59
c 1
b 0
f 0
dl 0
loc 97
rs 10

3 Methods

Rating   Name   Duplication   Size   Complexity  
A getClientCredentialsToken() 0 26 3
A getRequestAuthorization() 0 4 1
A me() 0 15 3
1
<?php
2
/**
3
 * Class Twitch
4
 *
5
 * @created      22.10.2017
6
 * @author       Smiley <[email protected]>
7
 * @copyright    2017 Smiley
8
 * @license      MIT
9
 */
10
11
namespace chillerlan\OAuth\Providers;
12
13
use chillerlan\HTTP\Utils\MessageUtil;
14
use chillerlan\HTTP\Utils\QueryUtil;
15
use chillerlan\OAuth\Core\{AccessToken, ClientCredentials, CSRFToken, OAuth2Provider, ProviderException, TokenRefresh};
16
use Psr\Http\Message\RequestInterface;
17
use Psr\Http\Message\ResponseInterface;
18
use function implode;
19
use function sprintf;
20
use const PHP_QUERY_RFC1738;
21
22
/**
23
 * @see https://dev.twitch.tv/docs/api/reference/
24
 * @see https://dev.twitch.tv/docs/authentication/
25
 */
26
class Twitch extends OAuth2Provider implements ClientCredentials, CSRFToken, TokenRefresh{
27
28
	public const SCOPE_ANALYTICS_READ_EXTENSIONS  = 'analytics:read:extensions';
29
	public const SCOPE_ANALYTICS_READ_GAMES       = 'analytics:read:games';
30
	public const SCOPE_BITS_READ                  = 'bits:read';
31
	public const SCOPE_CHANNEL_EDIT_COMMERCIAL    = 'channel:edit:commercial';
32
	public const SCOPE_CHANNEL_MANAGE_BROADCAST   = 'channel:manage:broadcast';
33
	public const SCOPE_CHANNEL_MANAGE_EXTENSIONS  = 'channel:manage:extensions';
34
	public const SCOPE_CHANNEL_MANAGE_REDEMPTIONS = 'channel:manage:redemptions';
35
	public const SCOPE_CHANNEL_MANAGE_VIDEOS      = 'channel:manage:videos';
36
	public const SCOPE_CHANNEL_READ_EDITORS       = 'channel:read:editors';
37
	public const SCOPE_CHANNEL_READ_HYPE_TRAIN    = 'channel:read:hype_train';
38
	public const SCOPE_CHANNEL_READ_REDEMPTIONS   = 'channel:read:redemptions';
39
	public const SCOPE_CHANNEL_READ_STREAM_KEY    = 'channel:read:stream_key';
40
	public const SCOPE_CHANNEL_READ_SUBSCRIPTIONS = 'channel:read:subscriptions';
41
	public const SCOPE_CLIPS_EDIT                 = 'clips:edit';
42
	public const SCOPE_MODERATION_READ            = 'moderation:read';
43
	public const SCOPE_USER_EDIT                  = 'user:edit';
44
	public const SCOPE_USER_EDIT_FOLLOWS          = 'user:edit:follows';
45
	public const SCOPE_USER_READ_BLOCKED_USERS    = 'user:read:blocked_users';
46
	public const SCOPE_USER_MANAGE_BLOCKED_USERS  = 'user:manage:blocked_users';
47
	public const SCOPE_USER_READ_BROADCAST        = 'user:read:broadcast';
48
	public const SCOPE_USER_READ_EMAIL            = 'user:read:email';
49
	public const SCOPE_USER_READ_SUBSCRIPTIONS    = 'user:read:subscriptions';
50
51
	protected string  $authURL                    = 'https://id.twitch.tv/oauth2/authorize';
52
	protected string  $accessTokenURL             = 'https://id.twitch.tv/oauth2/token';
53
	protected string  $apiURL                     = 'https://api.twitch.tv';
54
	protected ?string $userRevokeURL              = 'https://www.twitch.tv/settings/connections';
55
	protected ?string $revokeURL                  = 'https://id.twitch.tv/oauth2/revoke';
56
	protected ?string $apiDocs                    = 'https://dev.twitch.tv/docs/api/reference/';
57
	protected ?string $applicationURL             = 'https://dev.twitch.tv/console/apps/create';
58
	protected array   $authHeaders                = ['Accept' => 'application/vnd.twitchtv.v5+json'];
59
	protected array   $apiHeaders                 = ['Accept' => 'application/vnd.twitchtv.v5+json'];
60
61
	protected array   $defaultScopes              =  [
62
		self::SCOPE_USER_READ_EMAIL,
63
	];
64
65
	/**
66
	 * @see https://dev.twitch.tv/docs/authentication#oauth-client-credentials-flow-app-access-tokens
67
	 */
68
	public function getClientCredentialsToken(array $scopes = null):AccessToken{
69
		$params = [
70
			'client_id'     => $this->options->key,
71
			'client_secret' => $this->options->secret,
72
			'grant_type'    => 'client_credentials',
73
		];
74
75
		if($scopes !== null){
76
			$params['scope'] = implode($this->scopesDelimiter, $scopes);
77
		}
78
79
		$request = $this->requestFactory
80
			->createRequest('POST', $this->clientCredentialsTokenURL ?? $this->accessTokenURL)
81
			->withHeader('Content-Type', 'application/x-www-form-urlencoded')
82
			->withBody($this->streamFactory->createStream(QueryUtil::build($params, PHP_QUERY_RFC1738)))
83
		;
84
85
		foreach($this->authHeaders as $header => $value){
86
			$request = $request->withAddedHeader($header, $value);
87
		}
88
89
		$token = $this->parseTokenResponse($this->http->sendRequest($request));
90
91
		$this->storage->storeAccessToken($token, $this->serviceName);
92
93
		return $token;
94
	}
95
96
	/**
97
	 * @inheritDoc
98
	 */
99
	public function getRequestAuthorization(RequestInterface $request, AccessToken $token):RequestInterface{
100
		return $request
101
			->withHeader('Authorization', $this->authMethodHeader.' '.$token->accessToken)
102
			->withHeader('Client-ID', $this->options->key);
103
	}
104
105
	/**
106
	 * @inheritDoc
107
	 */
108
	public function me():ResponseInterface{
109
		$response = $this->request('/helix/users');
110
		$status   = $response->getStatusCode();
111
112
		if($status === 200){
113
			return $response;
114
		}
115
116
		$json = MessageUtil::decodeJSON($response);
117
118
		if(isset($json->error, $json->message)){
119
			throw new ProviderException($json->message);
120
		}
121
122
		throw new ProviderException(sprintf('user info error error HTTP/%s', $status));
123
	}
124
125
}
126