Passed
Push — main ( d68b9c...2c74b5 )
by smiley
01:38
created

SteamOpenID   A

Complexity

Total Complexity 9

Size/Duplication

Total Lines 108
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 9
eloc 45
c 1
b 0
f 0
dl 0
loc 108
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A getAuthURL() 0 13 1
A getAccessToken() 0 32 2
A parseTokenResponse() 0 15 4
A getRequestAuthorization() 0 10 2
1
<?php
2
/**
3
 * Class SteamOpenID
4
 *
5
 * @link https://steamcommunity.com/dev
6
 * @link https://partner.steamgames.com/doc/webapi_overview
7
 * @link https://steamwebapi.azurewebsites.net/
8
 *
9
 * @created      15.03.2021
10
 * @author       smiley <[email protected]>
11
 * @copyright    2021 smiley
12
 * @license      MIT
13
 */
14
15
namespace chillerlan\OAuth\Providers\Steam;
16
17
use chillerlan\OAuth\Core\{AccessToken, OAuthProvider, ProviderException};
18
use Psr\Http\Message\{RequestInterface, ResponseInterface, UriInterface};
19
20
use function chillerlan\HTTP\Psr7\merge_query;
21
use function explode, http_build_query, intval, parse_url, preg_replace, strpos;
22
23
use const PHP_URL_QUERY;
24
25
/**
26
 * @method \Psr\Http\Message\ResponseInterface playerServiceGetBadges(array $params = ['steamid'])
27
 * @method \Psr\Http\Message\ResponseInterface playerServiceGetCommunityBadgeProgress(array $params = ['steamid', 'badgeid'])
28
 * @method \Psr\Http\Message\ResponseInterface playerServiceGetOwnedGames(array $params = ['steamid', 'include_appinfo', 'include_played_free_games', 'appids_filter', 'include_free_sub', 'skip_unvetted_apps'])
29
 * @method \Psr\Http\Message\ResponseInterface playerServiceGetRecentlyPlayedGames(array $params = ['steamid', 'count'])
30
 * @method \Psr\Http\Message\ResponseInterface playerServiceGetSteamLevel(array $params = ['steamid'])
31
 * @method \Psr\Http\Message\ResponseInterface playerServiceIsPlayingSharedGame(array $params = ['steamid', 'appid_playing'])
32
 * @method \Psr\Http\Message\ResponseInterface steamAppsGetAppList()
33
 * @method \Psr\Http\Message\ResponseInterface steamAppsGetSDRConfig(array $params = ['appid', 'partner'])
34
 * @method \Psr\Http\Message\ResponseInterface steamAppsGetServersAtAddress(array $params = ['addr'])
35
 * @method \Psr\Http\Message\ResponseInterface steamAppsUpToDateCheck(array $params = ['appid', 'version'])
36
 * @method \Psr\Http\Message\ResponseInterface steamNewsGetNewsForApp(array $params = ['appid', 'maxlength', 'enddate', 'count', 'feeds', 'tags'])
37
 * @method \Psr\Http\Message\ResponseInterface steamUserGetFriendList(array $params = ['steamid', 'relationship'])
38
 * @method \Psr\Http\Message\ResponseInterface steamUserGetPlayerBans(array $params = ['steamids'])
39
 * @method \Psr\Http\Message\ResponseInterface steamUserGetPlayerSummaries(array $params = ['steamids'])
40
 * @method \Psr\Http\Message\ResponseInterface steamUserGetUserGroupList(array $params = ['steamid'])
41
 * @method \Psr\Http\Message\ResponseInterface steamUserResolveVanityURL(array $params = ['vanityurl', 'url_type'])
42
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetGlobalAchievementPercentagesForApp(array $params = ['gameid'])
43
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetGlobalStatsForGame(array $params = ['appid', 'count', 'name[0]', 'startdate', 'enddate'])
44
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetNumberOfCurrentPlayers(array $params = ['appid'])
45
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetPlayerAchievements(array $params = ['steamid', 'appid', 'l'])
46
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetSchemaForGame(array $params = ['appid', 'l'])
47
 * @method \Psr\Http\Message\ResponseInterface steamUserStatsGetUserStatsForGame(array $params = ['steamid', 'appid'])
48
 * @method \Psr\Http\Message\ResponseInterface steamWebAPIUtilGetServerInfo()
49
 * @method \Psr\Http\Message\ResponseInterface steamWebAPIUtilGetSupportedAPIList()
50
 * @method \Psr\Http\Message\ResponseInterface storeServiceGetAppList(array $params = ['if_modified_since', 'have_description_language', 'include_games', 'include_dlc', 'include_software', 'include_videos', 'include_hardware', 'last_appid', 'max_results'])
51
 */
52
class SteamOpenID extends OAuthProvider{
53
54
	protected string $authURL         = 'https://steamcommunity.com/openid/login';
55
	protected string $accessTokenURL  = 'https://steamcommunity.com/openid/login';
56
	protected ?string $apiURL         = 'https://api.steampowered.com';
57
	protected ?string $applicationURL = 'https://steamcommunity.com/dev/apikey';
58
	protected ?string $apiDocs        = 'https://developer.valvesoftware.com/wiki/Steam_Web_API';
59
	protected ?string $endpointMap    = SteamEndpoints::class;
60
61
	/**
62
	 * @param array|null $params
63
	 *
64
	 * @return \Psr\Http\Message\UriInterface
65
	 */
66
	public function getAuthURL(array $params = null):UriInterface{
67
68
		// we ignore user supplied params here
69
		$params = [
70
			'openid.ns'         => 'http://specs.openid.net/auth/2.0',
71
			'openid.mode'       => 'checkid_setup',
72
			'openid.return_to'  => $this->options->callbackURL,
73
			'openid.realm'      => $this->options->key,
74
			'openid.identity'   => 'http://specs.openid.net/auth/2.0/identifier_select',
75
			'openid.claimed_id' => 'http://specs.openid.net/auth/2.0/identifier_select',
76
		];
77
78
		return $this->uriFactory->createUri(merge_query($this->authURL, $params));
79
	}
80
81
	/**
82
	 * @param array $received
83
	 *
84
	 * @return \chillerlan\OAuth\Core\AccessToken
85
	 */
86
	public function getAccessToken(array $received):AccessToken{
87
88
		$body = [
89
			'openid.mode' => 'check_authentication',
90
			'openid.ns'   => 'http://specs.openid.net/auth/2.0',
91
			'openid.sig'  => $received['openid_sig'],
92
		];
93
94
		foreach(explode(',', $received['openid_signed']) as $item){
95
			$body['openid.'.$item] = $received['openid_'.$item];
96
		}
97
98
		$request = $this->requestFactory
99
			->createRequest('POST', $this->accessTokenURL)
100
			->withHeader('Content-Type', 'application/x-www-form-urlencoded')
101
			->withBody($this->streamFactory->createStream(http_build_query($body)));
102
103
		$token = $this->parseTokenResponse($this->http->sendRequest($request));
104
		$id    = preg_replace('/[^\d]/', '', $received['openid_claimed_id']);
105
106
		// as this method is intended for one-time authentication only we'll not receive a token.
107
		// instead we're gonna save the verified steam user id as token as it is required
108
		// for several "authenticated" endpoints.
109
		$token->accessToken = $id;
110
		$token->extraParams = [
111
			'claimed_id' => $received['openid_claimed_id'],
112
			'id_int'     => intval($id),
113
		];
114
115
		$this->storage->storeAccessToken($this->serviceName, $token);
116
117
		return $token;
118
	}
119
120
	/**
121
	 * @param \Psr\Http\Message\ResponseInterface $response
122
	 *
123
	 * @return \chillerlan\OAuth\Core\AccessToken
124
	 * @throws \chillerlan\OAuth\Core\ProviderException
125
	 */
126
	protected function parseTokenResponse(ResponseInterface $response):AccessToken{
127
		$data = explode("\x0a", (string)$response->getBody());
128
129
		if(!isset($data[1]) || strpos($data[1], 'is_valid') !== 0){
130
			throw new ProviderException('unable to parse token response');
131
		}
132
133
		if($data[1] !== 'is_valid:true'){
134
			throw new ProviderException('invalid id');
135
		}
136
137
		// the response is only validation, so we'll just return an empty token and add the id in the next step
138
		return new AccessToken([
139
			'provider' => $this->serviceName,
140
			'expires'  => AccessToken::EOL_NEVER_EXPIRES,
141
		]);
142
	}
143
144
	/**
145
	 * @param \Psr\Http\Message\RequestInterface $request
146
	 * @param \chillerlan\OAuth\Core\AccessToken $token
147
	 *
148
	 * @return \Psr\Http\Message\RequestInterface
149
	 */
150
	public function getRequestAuthorization(RequestInterface $request, AccessToken $token):RequestInterface{
151
		$uri    = (string)$request->getUri();
152
		$params = ['key' => $this->options->secret];
153
154
		// the steamid parameter does not necessarily specify the current user, so add it only when it's not already set
155
		if(strpos(parse_url($uri, PHP_URL_QUERY), 'steamid=') === false){ // php8: str_contains
156
			$params['steamid']= $token->accessToken;
157
		}
158
159
		return $request->withUri($this->uriFactory->createUri(merge_query($uri, $params)));
160
	}
161
162
}
163