├── .github └── workflows │ ├── psalm.yml │ └── tests.yml ├── .gitignore ├── LICENSE ├── README.md ├── UPGRADE.md ├── composer.json ├── config └── steam-auth.php ├── phpunit.xml ├── psalm.xml ├── src ├── Exceptions │ ├── Authentication │ │ ├── AuthenticationException.php │ │ ├── SteamIdNotFoundAuthenticationException.php │ │ └── SteamResponseNotValidAuthenticationException.php │ └── Validation │ │ ├── InvalidQueryValidationException.php │ │ ├── InvalidReturnToValidationException.php │ │ └── ValidationException.php ├── ServiceProvider.php ├── SteamAuthenticator.php └── SteamUserDto.php └── tests ├── AuthValidationTest.php ├── BuildAuthUrlTest.php ├── SteamResponsesTest.php └── TestCase.php /.github/workflows/psalm.yml: -------------------------------------------------------------------------------- 1 | name: Psalm Static Analysis 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.php' 7 | - 'psalm.xml' 8 | 9 | jobs: 10 | psalm: 11 | name: Psalm 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout Code 15 | uses: actions/checkout@v2 16 | 17 | - name: Setup PHP 18 | uses: shivammathur/setup-php@v2 19 | with: 20 | php-version: '8.2' 21 | 22 | - name: Install Dependencies 23 | run: | 24 | php -v 25 | composer install --prefer-dist --no-interaction 26 | 27 | - name: Run Psalm 28 | run: ./vendor/bin/psalm 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | tests: 7 | name: PHP ${{ matrix.php }}, Laravel ${{ matrix.laravel }} 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | php: [8.1, 8.2] 13 | laravel: [9.*, 10.*, 11.*, 12.*] 14 | 15 | steps: 16 | - name: Checkout Code 17 | uses: actions/checkout@v2 18 | 19 | - name: Setup PHP 20 | uses: shivammathur/setup-php@v2 21 | with: 22 | php-version: ${{ matrix.php }} 23 | 24 | - name: Install Dependencies 25 | run: | 26 | php -v 27 | composer install --prefer-dist --no-interaction 28 | 29 | - name: Execute Tests 30 | run: XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-clover coverage.xml 31 | 32 | - name: Upload coverage reports to Codecov 33 | uses: codecov/codecov-action@v3 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | /composer.lock 3 | /vendor/ 4 | /.phpunit.cache/ 5 | /.phpunit.result.cache 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ilia Lazarev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Steam Auth for Laravel 2 | [![Latest Stable Version](https://img.shields.io/packagist/v/ilzrv/laravel-steam-auth.svg)](https://packagist.org/packages/ilzrv/laravel-steam-auth) 3 | [![Total Downloads](https://img.shields.io/packagist/dt/ilzrv/laravel-steam-auth.svg)](https://packagist.org/packages/ilzrv/laravel-steam-auth) 4 | [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/ilzrv/laravel-steam-auth/tests.yml)](https://github.com/ilzrv/laravel-steam-auth/actions/workflows/tests.yml) 5 | [![Codecov](https://img.shields.io/codecov/c/github/ilzrv/laravel-steam-auth?token=MIEA87EZGP)](https://app.codecov.io/github/ilzrv/laravel-steam-auth) 6 | [![License](https://img.shields.io/github/license/ilzrv/laravel-steam-auth.svg)](https://packagist.org/packages/ilzrv/laravel-steam-auth) 7 | 8 | Package allows you to implement Steam authentication in your Laravel project. 9 | 10 | ## Requirements 11 | * Laravel 9+ 12 | * PHP 8.1+ 13 | 14 | ## Installation 15 | #### Install the package 16 | ```bash 17 | composer require ilzrv/laravel-steam-auth 18 | ``` 19 | 20 | #### Publish the config file 21 | ```bash 22 | php artisan vendor:publish --provider="Ilzrv\LaravelSteamAuth\ServiceProvider" 23 | ``` 24 | 25 | #### Setup Steam API Key(s) 26 | 27 | Add your Steam API key to your `.env` file. You can find it [here](https://steamcommunity.com/dev/apikey). 28 | 29 | *if you want to use multiple API keys just list them separated by commas* 30 | 31 | ``` 32 | STEAM_AUTH_API_KEYS=YourSteamApiKey1,YourSteamApiKey2 33 | ``` 34 | 35 | ## Tips 36 | 37 | #### Client Settings 38 | You can use any settings that your client supports, for example, a [Guzzle Proxy](https://docs.guzzlephp.org/en/latest/request-options.html#proxy): 39 | 40 | ```php 41 | 'socks5://user:password@192.168.1.1:1080', 56 | ]); 57 | 58 | $steamAuthenticator = new SteamAuthenticator( 59 | new Uri($request->getUri()), 60 | $client, 61 | $httpFactory, 62 | ); 63 | 64 | // Continuation of your code... 65 | } 66 | ``` 67 | 68 | #### Proxy Domain 69 | If you want to make a proxy domain. Update `redirect_url` inside `steam-auth.php` to your absolute address like `https://auth.test/login`. You can use different domains for the local environment and for production like this: 70 | 71 | ```php 72 | env('APP_ENV', 'production') == 'production' 78 | ? 'https://auth.test/login' 79 | : null, 80 | ]; 81 | ``` 82 | 83 | In the NGINX settings for proxy domain, you can specify the following: 84 | ``` 85 | server { 86 | listen 443 ssl http2; 87 | server_name auth.test; 88 | return 301 https://general.test$uri$is_args$args; 89 | } 90 | ``` 91 | 92 | ## Basic example 93 | 94 | In `routes/web.php`: 95 | 96 | ```php 97 | Route::get('login', \App\Http\Controllers\Auth\SteamAuthController::class); 98 | ``` 99 | 100 | Create a controller `SteamAuthController.php`: 101 | 102 | ```php 103 | getUri()), 133 | $client, 134 | $httpFactory, 135 | ); 136 | 137 | try { 138 | $steamAuthenticator->auth(); 139 | } catch (ValidationException|SteamResponseNotValidAuthenticationException) { 140 | return $redirector->to( 141 | $steamAuthenticator->buildAuthUrl() 142 | ); 143 | } 144 | 145 | $steamUser = $steamAuthenticator->getSteamUser(); 146 | 147 | $authManager->login( 148 | $this->firstOrCreate($steamUser), 149 | true 150 | ); 151 | 152 | return $redirector->to('/'); 153 | } 154 | 155 | private function firstOrCreate(SteamUserDto $steamUser): User 156 | { 157 | return User::firstOrCreate([ 158 | 'steam_id' => $steamUser->getSteamId(), 159 | ], [ 160 | 'name' => $steamUser->getPersonaName(), 161 | 'avatar' => $steamUser->getAvatarFull(), 162 | 'player_level' => $steamUser->getPlayerLevel(), 163 | // ...and other what you need 164 | ]); 165 | } 166 | } 167 | ``` 168 | -------------------------------------------------------------------------------- /UPGRADE.md: -------------------------------------------------------------------------------- 1 | # Upgrade guide 2 | 3 | ## 2.0 to 3.0 4 | - You should completely redefine the authentication process according to the README (for example your controller) 5 | - The configuration remains the same 6 | - Now the authenticator uses PSR abstractions 7 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ilzrv/laravel-steam-auth", 3 | "description": "Steam Auth for Laravel", 4 | "require": { 5 | "php": "^8.1", 6 | "illuminate/support": "^9.0|^10.0|^11.0|^12.0", 7 | "psr/http-message": "^2.0", 8 | "psr/http-client": "^1.0", 9 | "psr/http-factory": "^1.0" 10 | }, 11 | "require-dev": { 12 | "roave/security-advisories": "dev-latest", 13 | "phpunit/phpunit": "^10.0|^11.0", 14 | "orchestra/testbench": "7.x|^8.5", 15 | "vimeo/psalm": "^5.12" 16 | }, 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "Ilia Lazarev", 21 | "email": "me@ilzrv.com" 22 | } 23 | ], 24 | "autoload": { 25 | "psr-4": { 26 | "Ilzrv\\LaravelSteamAuth\\": "src" 27 | } 28 | }, 29 | "autoload-dev": { 30 | "psr-4": { 31 | "Ilzrv\\LaravelSteamAuth\\Tests\\": "tests/" 32 | } 33 | }, 34 | "extra": { 35 | "laravel": { 36 | "providers": [ 37 | "Ilzrv\\LaravelSteamAuth\\ServiceProvider" 38 | ] 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /config/steam-auth.php: -------------------------------------------------------------------------------- 1 | null, 16 | 17 | /* 18 | |-------------------------------------------------------------------------- 19 | | Getting Steam User Level 20 | |-------------------------------------------------------------------------- 21 | | 22 | | Defines need to get the user level. 23 | | 24 | | This makes an additional request in the Steam API. 25 | | 26 | */ 27 | 'getting_level' => false, 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Steam API Keys 32 | |-------------------------------------------------------------------------- 33 | | 34 | | List of Steam API keys for reducing 35 | | requests from a single key. 36 | | 37 | */ 38 | 'api_keys' => explode(',', env('STEAM_AUTH_API_KEYS')), 39 | 40 | ]; 41 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | 20 | src 21 | 22 | 23 | src/SteamUserDto.php 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /psalm.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/Exceptions/Authentication/AuthenticationException.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__ . '/../config/steam-auth.php', 'steam-auth'); 15 | } 16 | 17 | /** 18 | * Bootstrap any application services. 19 | */ 20 | public function boot(): void 21 | { 22 | $this->publishes([ 23 | __DIR__ . '/../config/steam-auth.php' => $this->app->configPath('steam-auth.php'), 24 | ]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/SteamAuthenticator.php: -------------------------------------------------------------------------------- 1 | validateRequest(); 45 | $this->validateReturnToUrl(); 46 | 47 | $response = $this->httpClient->sendRequest( 48 | $this->requestFactory->createRequest('GET', self::OPENID_URL . '?' . $this->buildOpenIdRequestQuery()), 49 | ); 50 | 51 | $contents = $response->getBody()->getContents(); 52 | 53 | if (preg_match("#is_valid\s*:\s*true#i", $contents) !== 1) { 54 | throw new SteamResponseNotValidAuthenticationException( 55 | sprintf('Steam response contains invalid content: "%s"', $contents) 56 | ); 57 | } 58 | 59 | $query = $this->parseUriQueryString(); 60 | 61 | preg_match( 62 | "#^https?://steamcommunity.com/openid/id/([0-9]{17,25})#", 63 | $query['openid_claimed_id'], 64 | $matches 65 | ); 66 | 67 | if (!isset($matches[1]) || !is_numeric($matches[1])) { 68 | throw new SteamIdNotFoundAuthenticationException(); 69 | } 70 | 71 | $this->loadSteamUser($matches[1]); 72 | } 73 | 74 | /** 75 | * @throws ClientExceptionInterface 76 | * @throws JsonException 77 | */ 78 | private function loadSteamUser(string $steamId): void 79 | { 80 | $steamDataResponse = $this->httpClient->sendRequest( 81 | $this->requestFactory->createRequest('GET', sprintf(self::STEAM_DATA_URL, $this->getApiKey(), $steamId)), 82 | ); 83 | 84 | $steamData = json_decode( 85 | $steamDataResponse->getBody()->getContents(), 86 | true, 87 | 512, 88 | JSON_THROW_ON_ERROR 89 | )['response']['players'][0]; 90 | 91 | if (config('steam-auth.getting_level')) { 92 | $userLevelResponse = $this->httpClient->sendRequest( 93 | $this->requestFactory->createRequest( 94 | 'GET', 95 | sprintf(self::STEAM_LEVEL_URL, $this->getApiKey(), $steamId) 96 | ) 97 | ); 98 | 99 | $steamData = array_merge( 100 | $steamData, 101 | json_decode($userLevelResponse->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR)['response'] 102 | ); 103 | } 104 | 105 | $this->steamUserDto = SteamUserDto::create($steamData); 106 | } 107 | 108 | private function getApiKey(): string 109 | { 110 | $apiKeys = config('steam-auth.api_keys'); 111 | 112 | return $apiKeys[array_rand($apiKeys)]; 113 | } 114 | 115 | private function buildOpenIdRequestQuery(): string 116 | { 117 | $query = $this->parseUriQueryString(); 118 | 119 | $params = [ 120 | 'openid.assoc_handle' => $query['openid_assoc_handle'], 121 | 'openid.signed' => $query['openid_signed'], 122 | 'openid.sig' => $query['openid_sig'], 123 | 'openid.ns' => 'http://specs.openid.net/auth/2.0', 124 | 'openid.mode' => 'check_authentication', 125 | ]; 126 | 127 | $signed = explode(',', $query['openid_signed']); 128 | 129 | foreach ($signed as $item) { 130 | $params['openid.' . $item] = $query['openid_' . str_replace('.', '_', $item)] ?? null; 131 | } 132 | 133 | return http_build_query($params); 134 | } 135 | 136 | public function getSteamUser(): ?SteamUserDto 137 | { 138 | return $this->steamUserDto; 139 | } 140 | 141 | /** 142 | * @throws InvalidQueryValidationException 143 | */ 144 | private function validateRequest(): void 145 | { 146 | $query = $this->parseUriQueryString(); 147 | 148 | $params = [ 149 | 'openid_assoc_handle', 150 | 'openid_signed', 151 | 'openid_sig', 152 | 'openid_return_to', 153 | 'openid_claimed_id', 154 | ]; 155 | 156 | foreach ($params as $param) { 157 | if (!isset($query[$param])) { 158 | throw InvalidQueryValidationException::invalidQuery($param); 159 | } 160 | } 161 | } 162 | 163 | /** 164 | * @throws InvalidReturnToValidationException 165 | */ 166 | private function validateReturnToUrl(): void 167 | { 168 | $query = $this->parseUriQueryString(); 169 | 170 | if ($this->buildRedirectUrl() !== $query['openid_return_to']) { 171 | throw InvalidReturnToValidationException::invalidReturnTo(); 172 | } 173 | } 174 | 175 | private function parseUriQueryString(): array 176 | { 177 | parse_str($this->requestUri->getQuery(), $result); 178 | 179 | return $result; 180 | } 181 | 182 | public function buildAuthUrl(): string 183 | { 184 | $redirectUrl = $this->buildRedirectUrl(); 185 | 186 | $params = [ 187 | 'openid.ns' => 'http://specs.openid.net/auth/2.0', 188 | 'openid.mode' => 'checkid_setup', 189 | 'openid.return_to' => $redirectUrl, 190 | 'openid.realm' => parse_url($redirectUrl, PHP_URL_SCHEME) . '://' . parse_url($redirectUrl, PHP_URL_HOST), 191 | 'openid.identity' => 'http://specs.openid.net/auth/2.0/identifier_select', 192 | 'openid.claimed_id' => 'http://specs.openid.net/auth/2.0/identifier_select', 193 | ]; 194 | 195 | return self::OPENID_URL . '?' . http_build_query($params, '', '&'); 196 | } 197 | 198 | private function buildRedirectUrl(): string 199 | { 200 | $redirectUrl = config('steam-auth.redirect_url'); 201 | 202 | if (is_string($redirectUrl) && is_string($buildRedirectUrl = url($redirectUrl))) { 203 | return $buildRedirectUrl; 204 | } 205 | 206 | return $this->requestUri->getScheme() 207 | . '://' 208 | . $this->requestUri->getAuthority() 209 | . $this->requestUri->getPath(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/SteamUserDto.php: -------------------------------------------------------------------------------- 1 | steamId; 56 | } 57 | 58 | public function getCommunityVisibilityState(): ?int 59 | { 60 | return $this->communityVisibilityState; 61 | } 62 | 63 | public function getProfileState(): ?int 64 | { 65 | return $this->profileState; 66 | } 67 | 68 | public function getPersonaName(): ?string 69 | { 70 | return $this->personaName; 71 | } 72 | 73 | public function getCommentPermission(): ?int 74 | { 75 | return $this->commentPermission; 76 | } 77 | 78 | public function getProfileUrl(): ?string 79 | { 80 | return $this->profileUrl; 81 | } 82 | 83 | public function getAvatar(): ?string 84 | { 85 | return $this->avatar; 86 | } 87 | 88 | public function getAvatarMedium(): ?string 89 | { 90 | return $this->avatarMedium; 91 | } 92 | 93 | public function getAvatarFull(): ?string 94 | { 95 | return $this->avatarFull; 96 | } 97 | 98 | public function getAvatarHash(): ?string 99 | { 100 | return $this->avatarHash; 101 | } 102 | 103 | public function getLastLogoff(): ?int 104 | { 105 | return $this->lastLogoff; 106 | } 107 | 108 | public function getPersonaState(): ?int 109 | { 110 | return $this->personaState; 111 | } 112 | 113 | public function getPrimaryClanId(): ?string 114 | { 115 | return $this->primaryClanId; 116 | } 117 | 118 | public function getTimeCreated(): ?int 119 | { 120 | return $this->timeCreated; 121 | } 122 | 123 | public function getPersonaStateFlags(): ?int 124 | { 125 | return $this->personaStateFlags; 126 | } 127 | 128 | public function getLocCountryCode(): ?string 129 | { 130 | return $this->locCountryCode; 131 | } 132 | 133 | public function getPlayerLevel(): ?int 134 | { 135 | return $this->playerLevel; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /tests/AuthValidationTest.php: -------------------------------------------------------------------------------- 1 | createMock(UriInterface::class); 33 | $uri->expects($this->once())->method('getQuery')->willReturn($query); 34 | 35 | $steamAuth = new SteamAuthenticator( 36 | $uri, 37 | $this->createMock(ClientInterface::class), 38 | $this->createMock(RequestFactoryInterface::class), 39 | ); 40 | 41 | $this->expectException(InvalidQueryValidationException::class); 42 | $this->expectExceptionMessage('The "' . $param . '" parameter is required'); 43 | 44 | $steamAuth->auth(); 45 | } 46 | 47 | public static function provideRequiredParams(): array 48 | { 49 | return [ 50 | ['openid_assoc_handle', self::buildHttpQuery('openid_assoc_handle')], 51 | ['openid_signed', self::buildHttpQuery('openid_signed')], 52 | ['openid_sig', self::buildHttpQuery('openid_sig')], 53 | ['openid_return_to', self::buildHttpQuery('openid_return_to')], 54 | ['openid_claimed_id', self::buildHttpQuery('openid_claimed_id')], 55 | ]; 56 | } 57 | 58 | /** 59 | * @throws Exception 60 | * @throws ClientExceptionInterface 61 | * @throws InvalidQueryValidationException 62 | * @throws AuthenticationException 63 | * @throws JsonException 64 | */ 65 | public function testValidateReturnToUrl(): void 66 | { 67 | $uri = $this->createMock(UriInterface::class); 68 | $uri->expects($this->exactly(2))->method('getQuery')->willReturn(self::buildHttpQuery()); 69 | 70 | $steamAuth = new SteamAuthenticator( 71 | $uri, 72 | $this->createMock(ClientInterface::class), 73 | $this->createMock(RequestFactoryInterface::class), 74 | ); 75 | 76 | $this->expectException(InvalidReturnToValidationException::class); 77 | $this->expectExceptionMessage('openid_return_to does not match redirect url'); 78 | 79 | $steamAuth->auth(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/BuildAuthUrlTest.php: -------------------------------------------------------------------------------- 1 | app['config']->set('steam-auth.redirect_url', null); 16 | 17 | $steamAuthenticator = new SteamAuthenticator( 18 | $this->createUriMock('example.test'), 19 | $this->createMock(ClientInterface::class), 20 | $this->createMock(RequestFactoryInterface::class), 21 | ); 22 | 23 | $uri = 'https://steamcommunity.com/openid/login?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=checkid_setup&openid.return_to=https%3A%2F%2Fexample.test%2Flogin&openid.realm=https%3A%2F%2Fexample.test&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select'; 24 | 25 | $this->assertSame( 26 | $uri, 27 | $steamAuthenticator->buildAuthUrl() 28 | ); 29 | } 30 | 31 | public function testAuthUrlWithRedirectUrl(): void 32 | { 33 | $this->app['config']->set('steam-auth.redirect_url', 'https://used-example.test/login'); 34 | 35 | $steamAuthenticator = new SteamAuthenticator( 36 | $this->createUriMock('unused-example.test'), 37 | $this->createMock(ClientInterface::class), 38 | $this->createMock(RequestFactoryInterface::class), 39 | ); 40 | 41 | $uri = 'https://steamcommunity.com/openid/login?openid.ns=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0&openid.mode=checkid_setup&openid.return_to=https%3A%2F%2Fused-example.test%2Flogin&openid.realm=https%3A%2F%2Fused-example.test&openid.identity=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select&openid.claimed_id=http%3A%2F%2Fspecs.openid.net%2Fauth%2F2.0%2Fidentifier_select'; 42 | 43 | $this->assertSame( 44 | $uri, 45 | $steamAuthenticator->buildAuthUrl() 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/SteamResponsesTest.php: -------------------------------------------------------------------------------- 1 | createResponseMock( 22 | <<createMock(ClientInterface::class); 29 | $client->method('sendRequest')->willReturn($response); 30 | 31 | $steamAuthenticator = new SteamAuthenticator( 32 | $this->createUriMock('example.test'), 33 | $client, 34 | $this->createMock(RequestFactoryInterface::class), 35 | ); 36 | 37 | $this->expectException(SteamResponseNotValidAuthenticationException::class); 38 | 39 | $steamAuthenticator->auth(); 40 | } 41 | 42 | public function testSteamIdNotFoundResponse(): void 43 | { 44 | $response = $this->createResponseMock( 45 | <<createMock(ClientInterface::class); 52 | $client->method('sendRequest')->willReturn($response); 53 | 54 | $steamAuthenticator = new SteamAuthenticator( 55 | $this->createUriMock('example.test'), 56 | $client, 57 | $this->createMock(RequestFactoryInterface::class), 58 | ); 59 | 60 | $this->expectException(SteamIdNotFoundAuthenticationException::class); 61 | 62 | $steamAuthenticator->auth(); 63 | } 64 | 65 | public function testValidSteamResponseWithoutPlayerLevel(): void 66 | { 67 | $this->app['config']->set('steam-auth.getting_level', false); 68 | 69 | $response1 = $this->createResponseMock( 70 | <<createResponseMock( 77 | <<createMock(ClientInterface::class); 83 | $client->method('sendRequest')->willReturnOnConsecutiveCalls($response1, $response2); 84 | 85 | $steamAuthenticator = new SteamAuthenticator( 86 | $this->createUriMockWithOpenidClaimedId('76561198019153518'), 87 | $client, 88 | $this->createMock(RequestFactoryInterface::class), 89 | ); 90 | 91 | $steamAuthenticator->auth(); 92 | 93 | $this->assertInstanceOf(SteamUserDto::class, $steamAuthenticator->getSteamUser()); 94 | } 95 | 96 | public function testValidSteamResponseWithPlayerLevel(): void 97 | { 98 | $this->app['config']->set('steam-auth.getting_level', true); 99 | 100 | $response1 = $this->createResponseMock( 101 | <<createResponseMock( 108 | <<createResponseMock( 114 | <<createMock(ClientInterface::class); 120 | $client->method('sendRequest')->willReturnOnConsecutiveCalls($response1, $response2, $response3); 121 | 122 | $steamAuthenticator = new SteamAuthenticator( 123 | $this->createUriMockWithOpenidClaimedId('76561198019153518'), 124 | $client, 125 | $this->createMock(RequestFactoryInterface::class), 126 | ); 127 | 128 | $steamAuthenticator->auth(); 129 | 130 | $this->assertInstanceOf(SteamUserDto::class, $steamAuthenticator->getSteamUser()); 131 | $this->assertSame(44, $steamAuthenticator->getSteamUser()->getPlayerLevel()); 132 | } 133 | 134 | private function createResponseMock(string $contents): ResponseInterface 135 | { 136 | $body = $this->createMock(StreamInterface::class); 137 | $body->method('getContents')->willReturn($contents); 138 | 139 | $response = $this->createMock(ResponseInterface::class); 140 | $response->method('getBody')->willReturn($body); 141 | 142 | return $response; 143 | } 144 | 145 | private function createUriMockWithOpenidClaimedId(string $steamId): UriInterface 146 | { 147 | $uri = $this->createMock(UriInterface::class); 148 | 149 | $httpQuery = self::buildHttpQuery(null, [ 150 | 'openid_return_to' => 'https://example.test/login', 151 | 'openid_claimed_id' => 'https://steamcommunity.com/openid/id/' . $steamId, 152 | ]); 153 | 154 | $uri->method('getScheme')->willReturn('https'); 155 | $uri->method('getAuthority')->willReturn('example.test'); 156 | $uri->method('getPath')->willReturn('/login'); 157 | $uri->method('getQuery')->willReturn($httpQuery); 158 | 159 | return $uri; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | > 19 | */ 20 | protected function getPackageProviders($app) 21 | { 22 | return [ 23 | ServiceProvider::class, 24 | ]; 25 | } 26 | 27 | protected static function buildHttpQuery( 28 | string $without = null, 29 | array $replace = [], 30 | ): string { 31 | $params = [ 32 | 'openid_assoc_handle' => 'data', 33 | 'openid_signed' => 'data', 34 | 'openid_sig' => 'data', 35 | 'openid_return_to' => 'data', 36 | 'openid_claimed_id' => 'data', 37 | ]; 38 | 39 | $params = array_replace($params, $replace); 40 | 41 | unset($params[$without]); 42 | 43 | return http_build_query($params); 44 | } 45 | 46 | /** 47 | * @throws Exception 48 | */ 49 | protected function createUriMock( 50 | string $authority, 51 | ): UriInterface { 52 | $uri = $this->createMock(UriInterface::class); 53 | 54 | $uri->method('getScheme')->willReturn('https'); 55 | $uri->method('getAuthority')->willReturn($authority); 56 | $uri->method('getPath')->willReturn('/login'); 57 | $uri->method('getQuery')->willReturn( 58 | self::buildHttpQuery(null, ['openid_return_to' => "https://$authority/login"]) 59 | ); 60 | 61 | return $uri; 62 | } 63 | } 64 | 65 | --------------------------------------------------------------------------------