Passed
Push — main ( 129f19...5ed128 )
by smiley
31:18
created

Mastodon::getAccessToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 27
rs 9.7
cc 2
nc 2
nop 2
1
<?php
2
/**
3
 * Class Mastodon
4
 *
5
 * @link https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md
6
 * @link https://github.com/tootsuite/documentation/blob/master/Using-the-API/OAuth-details.md
7
 *
8
 * @created      19.08.2018
9
 * @author       smiley <[email protected]>
10
 * @copyright    2018 smiley
11
 * @license      MIT
12
 */
13
14
namespace chillerlan\OAuth\Providers\Mastodon;
15
16
use chillerlan\OAuth\Core\{AccessToken, CSRFToken, OAuth2Provider, TokenRefresh};
17
use chillerlan\OAuth\OAuthException;
18
19
use function array_merge, http_build_query, parse_url;
20
use const PHP_QUERY_RFC1738;
21
22
/**
23
 * @method \Psr\Http\Message\ResponseInterface acceptFollowRequest(string $request_id)
24
 * @method \Psr\Http\Message\ResponseInterface addListMembers(string $list_id, array $body = ['account_ids'])
25
 * @method \Psr\Http\Message\ResponseInterface addPushSubscription(array $body = ['subscription', 'data'])
26
 * @method \Psr\Http\Message\ResponseInterface block(string $account_id)
27
 * @method \Psr\Http\Message\ResponseInterface blockDomain(array $body = ['domain'])
28
 * @method \Psr\Http\Message\ResponseInterface boost(string $toot_id)
29
 * @method \Psr\Http\Message\ResponseInterface clearNotifications()
30
 * @method \Psr\Http\Message\ResponseInterface createApp(array $body = ['client_name', 'redirect_uris', 'scopes', 'website'])
31
 * @method \Psr\Http\Message\ResponseInterface createAttachment(array $body = ['file', 'description', 'focus'])
32
 * @method \Psr\Http\Message\ResponseInterface createFilter(array $body = ['phrase', 'context', 'irreversible', 'whole_word', 'expires_in'])
33
 * @method \Psr\Http\Message\ResponseInterface createList(array $body = ['title'])
34
 * @method \Psr\Http\Message\ResponseInterface deleteFilter(string $filter_id)
35
 * @method \Psr\Http\Message\ResponseInterface deleteFollowSuggestion(string $account_id)
36
 * @method \Psr\Http\Message\ResponseInterface deleteList(string $list_id)
37
 * @method \Psr\Http\Message\ResponseInterface deletePushSubscription()
38
 * @method \Psr\Http\Message\ResponseInterface deleteToot(string $toot_id)
39
 * @method \Psr\Http\Message\ResponseInterface dismissNotification(array $body = ['notification_id'])
40
 * @method \Psr\Http\Message\ResponseInterface favourite(string $toot_id)
41
 * @method \Psr\Http\Message\ResponseInterface follow(string $account_id, array $params = ['reblogs'])
42
 * @method \Psr\Http\Message\ResponseInterface follows(array $body = ['uri'])
43
 * @method \Psr\Http\Message\ResponseInterface getAccount(string $account_id)
44
 * @method \Psr\Http\Message\ResponseInterface getCurrentUser()
45
 * @method \Psr\Http\Message\ResponseInterface getCurrentUserBlocks(array $params = ['max_id', 'since_id', 'limit'])
46
 * @method \Psr\Http\Message\ResponseInterface getCurrentUserDomainBlocks(array $params = ['max_id', 'since_id', 'limit'])
47
 * @method \Psr\Http\Message\ResponseInterface getCurrentUserRelationships(array $params = ['account_id'])
48
 * @method \Psr\Http\Message\ResponseInterface getFilter(string $filter_id)
49
 * @method \Psr\Http\Message\ResponseInterface getFilters()
50
 * @method \Psr\Http\Message\ResponseInterface getFollowRequests(array $params = ['max_id', 'since_id', 'limit'])
51
 * @method \Psr\Http\Message\ResponseInterface getFollowSuggestions()
52
 * @method \Psr\Http\Message\ResponseInterface getFollowers(string $account_id, array $params = ['max_id', 'since_id', 'limit'])
53
 * @method \Psr\Http\Message\ResponseInterface getFollowing(string $account_id, array $params = ['max_id', 'since_id', 'limit'])
54
 * @method \Psr\Http\Message\ResponseInterface getHomeTimeline(array $params = ['local', 'only_media', 'max_id', 'since_id', 'limit'])
55
 * @method \Psr\Http\Message\ResponseInterface getInstance()
56
 * @method \Psr\Http\Message\ResponseInterface getInstanceCustomEmojis()
57
 * @method \Psr\Http\Message\ResponseInterface getList(string $list_id)
58
 * @method \Psr\Http\Message\ResponseInterface getListMembers(string $list_id, array $params = ['max_id', 'since_id', 'limit'])
59
 * @method \Psr\Http\Message\ResponseInterface getListTimeline(string $list_id, array $params = ['local', 'only_media', 'max_id', 'since_id', 'limit'])
60
 * @method \Psr\Http\Message\ResponseInterface getLists()
61
 * @method \Psr\Http\Message\ResponseInterface getListsByMembership(string $account_id)
62
 * @method \Psr\Http\Message\ResponseInterface getMutes(string $list_id, array $params = ['max_id', 'since_id', 'limit'])
63
 * @method \Psr\Http\Message\ResponseInterface getNotification(string $notification_id)
64
 * @method \Psr\Http\Message\ResponseInterface getNotifications(string $list_id, array $params = ['exclude_types', 'max_id', 'since_id', 'limit'])
65
 * @method \Psr\Http\Message\ResponseInterface getPublicTimeline(array $params = ['local', 'only_media', 'max_id', 'since_id', 'limit'])
66
 * @method \Psr\Http\Message\ResponseInterface getPushSubscriptionStatus()
67
 * @method \Psr\Http\Message\ResponseInterface getReports()
68
 * @method \Psr\Http\Message\ResponseInterface getStatuses(string $account_id, array $params = ['only_media', 'pinned', 'exclude_replies', 'max_id', 'since_id', 'limit'])
69
 * @method \Psr\Http\Message\ResponseInterface getTagTimeline(string $hashtag, array $params = ['local', 'only_media', 'max_id', 'since_id', 'limit'])
70
 * @method \Psr\Http\Message\ResponseInterface getToot(string $toot_id)
71
 * @method \Psr\Http\Message\ResponseInterface getTootBoostedBy(string $toot_id, array $params = ['max_id', 'since_id', 'limit'])
72
 * @method \Psr\Http\Message\ResponseInterface getTootCard(string $toot_id)
73
 * @method \Psr\Http\Message\ResponseInterface getTootContext(string $toot_id)
74
 * @method \Psr\Http\Message\ResponseInterface getTootFavouritedBy(string $toot_id, array $params = ['max_id', 'since_id', 'limit'])
75
 * @method \Psr\Http\Message\ResponseInterface mute(string $account_id, array $body = ['notifications'])
76
 * @method \Psr\Http\Message\ResponseInterface muteToot(string $toot_id)
77
 * @method \Psr\Http\Message\ResponseInterface pinToot(string $toot_id)
78
 * @method \Psr\Http\Message\ResponseInterface rejectFollowRequest(string $request_id)
79
 * @method \Psr\Http\Message\ResponseInterface removeListMembers(string $list_id, array $body = ['account_ids'])
80
 * @method \Psr\Http\Message\ResponseInterface report(array $body = ['account_id', 'status_ids', 'comment'])
81
 * @method \Psr\Http\Message\ResponseInterface search(array $params = ['q', 'resolve'])
82
 * @method \Psr\Http\Message\ResponseInterface searchV2(array $params = ['q', 'resolve'])
83
 * @method \Psr\Http\Message\ResponseInterface toot(array $body = ['status', 'in_reply_to_id', 'media_ids', 'sensitive', 'spoiler_text', 'visibility', 'language'])
84
 * @method \Psr\Http\Message\ResponseInterface unblock(string $account_id)
85
 * @method \Psr\Http\Message\ResponseInterface unblockDomain(array $body = ['domain'])
86
 * @method \Psr\Http\Message\ResponseInterface unboost(string $toot_id)
87
 * @method \Psr\Http\Message\ResponseInterface unfavourite(string $toot_id)
88
 * @method \Psr\Http\Message\ResponseInterface unfollow(string $account_id)
89
 * @method \Psr\Http\Message\ResponseInterface unmute(string $account_id)
90
 * @method \Psr\Http\Message\ResponseInterface unmuteToot(string $toot_id)
91
 * @method \Psr\Http\Message\ResponseInterface unpinToot(string $toot_id)
92
 * @method \Psr\Http\Message\ResponseInterface updateAttachment(string $attachment_id, array $body = ['description', 'focus'])
93
 * @method \Psr\Http\Message\ResponseInterface updateCurrentUser(array $body = ['display_name', 'note', 'avatar', 'header', 'locked', 'source', 'fields_attributes'])
94
 * @method \Psr\Http\Message\ResponseInterface updateFilter(string $filter_id, array $body = ['phrase', 'context', 'irreversible', 'whole_word', 'expires_in'])
95
 * @method \Psr\Http\Message\ResponseInterface updateList(string $list_id, array $body = ['title'])
96
 * @method \Psr\Http\Message\ResponseInterface updatePushSubscription(array $body = ['data'])
97
 * @method \Psr\Http\Message\ResponseInterface userSearch(array $params = ['q', 'limit', 'following'])
98
 */
99
class Mastodon extends OAuth2Provider implements CSRFToken, TokenRefresh{
100
101
	public const SCOPE_READ   = 'read';
102
	public const SCOPE_WRITE  = 'write';
103
	public const SCOPE_FOLLOW = 'follow';
104
	public const SCOPE_PUSH   = 'push';
105
106
	protected ?string $endpointMap    = MastodonEndpoints::class;
107
	protected ?string $apiDocs        = 'https://docs.joinmastodon.org/api/guidelines/';
108
109
	protected array $defaultScopes    = [
110
		Mastodon::SCOPE_READ,
111
		Mastodon::SCOPE_FOLLOW,
112
	];
113
114
	protected string $instance = '';
115
116
	/**
117
	 * set the internal URLs for the given Mastodon instance
118
	 */
119
	public function setInstance(string $instance):Mastodon{
120
		$instance = parse_url($instance);
121
122
		if(!isset($instance['scheme']) || !isset($instance['host'])){
123
			throw new OAuthException('invalid instance URL');
124
		}
125
126
		// @todo: check if host exists/responds
127
128
		$this->instance = $instance['scheme'].'://'.$instance['host'];
129
130
		$this->apiURL         = $this->instance.'/api';
131
		$this->authURL        = $this->instance.'/oauth/authorize';
132
		$this->accessTokenURL = $this->instance.'/oauth/token';
133
		$this->userRevokeURL  = $this->instance.'/oauth/authorized_applications';
134
		$this->applicationURL = $this->instance.'/settings/applications';
135
136
		return $this;
137
	}
138
139
	/**
140
	 * @inheritDoc
141
	 */
142
	public function getAccessToken(string $code, string $state = null):AccessToken{
143
144
		$body = [
145
			'client_id'     => $this->options->key,
146
			'client_secret' => $this->options->secret,
147
			'code'          => $code,
148
			'grant_type'    => 'authorization_code',
149
			'redirect_uri'  => $this->options->callbackURL,
150
		];
151
152
		$request = $this->requestFactory
153
			->createRequest('POST', $this->accessTokenURL)
154
			->withHeader('Content-Type', 'application/x-www-form-urlencoded')
155
			->withHeader('Accept-Encoding', 'identity')
156
			->withBody($this->streamFactory->createStream(http_build_query($body, '', '&', PHP_QUERY_RFC1738)));
157
158
		foreach($this->authHeaders as $header => $value){
159
			$request = $request->withHeader($header, $value);
160
		}
161
162
		$token = $this->parseTokenResponse($this->http->sendRequest($request));
163
		// store the instance the token belongs to
164
		$token->extraParams = array_merge($token->extraParams, ['instance' => $this->instance]);
165
166
		$this->storage->storeAccessToken($this->serviceName, $token);
167
168
		return $token;
169
	}
170
171
}
172