friendship ended with social-app. php is my new best friend
1<?php 2/** 3 * Trait TokenInvalidateTrait 4 * 5 * @created 19.09.2024 6 * @author smiley <smiley@chillerlan.net> 7 * @copyright 2024 smiley 8 * @license MIT 9 */ 10declare(strict_types=1); 11 12namespace chillerlan\OAuth\Core; 13 14use chillerlan\HTTP\Utils\MessageUtil; 15use chillerlan\OAuth\Providers\ProviderException; 16use Psr\Http\Message\ResponseInterface; 17use function in_array; 18use function sprintf; 19use function str_contains; 20use function strtolower; 21use function trim; 22 23/** 24 * Implements token invalidation functionality 25 * 26 * @see \chillerlan\OAuth\Core\TokenInvalidate 27 */ 28trait TokenInvalidateTrait{ 29 30 /** 31 * implements TokenInvalidate::invalidateAccessToken() 32 * 33 * @see \chillerlan\OAuth\Core\TokenInvalidate::invalidateAccessToken() 34 * @throws \chillerlan\OAuth\Providers\ProviderException 35 */ 36 public function invalidateAccessToken(AccessToken|null $token = null, string|null $type = null):bool{ 37 $type = strtolower(trim(($type ?? 'access_token'))); 38 39 // @link https://datatracker.ietf.org/doc/html/rfc7009#section-2.1 40 if(!in_array($type, ['access_token', 'refresh_token'], true)){ 41 throw new ProviderException(sprintf('invalid token type "%s"', $type)); // @codeCoverageIgnore 42 } 43 44 $tokenToInvalidate = ($token ?? $this->storage->getAccessToken($this->name)); 45 $body = $this->getInvalidateAccessTokenBodyParams($tokenToInvalidate, $type); 46 $response = $this->sendTokenInvalidateRequest($this->revokeURL, $body); 47 48 // some endpoints may return 204, others 200 with empty body 49 if(in_array($response->getStatusCode(), [200, 204], true)){ 50 51 // if the token was given via parameter it cannot be deleted from storage 52 if($token === null){ 53 $this->storage->clearAccessToken($this->name); 54 } 55 56 return true; 57 } 58 59 // ok, let's see if we got a response body 60 // @link https://datatracker.ietf.org/doc/html/rfc7009#section-2.2.1 61 if(str_contains($response->getHeaderLine('content-type'), 'json')){ 62 $json = MessageUtil::decodeJSON($response, true); 63 64 if(isset($json['error'])){ 65 throw new ProviderException($json['error']); 66 } 67 } 68 69 return false; 70 } 71 72 /** 73 * Prepares the body for a token revocation request 74 * 75 * @see \chillerlan\OAuth\Core\OAuth2Provider::invalidateAccessToken() 76 * 77 * @return array<string, scalar|bool|null> 78 */ 79 protected function getInvalidateAccessTokenBodyParams(AccessToken $token, string $type):array{ 80 return [ 81 'token' => $token->accessToken, 82 'token_type_hint' => $type, 83 ]; 84 } 85 86 /** 87 * Prepares and sends a request to the token invalidation endpoint 88 * 89 * @see \chillerlan\OAuth\Core\OAuth2Provider::invalidateAccessToken() 90 * 91 * @param array<string, scalar|bool|null> $body 92 */ 93 protected function sendTokenInvalidateRequest(string $url, array $body):ResponseInterface{ 94 95 $request = $this->requestFactory 96 ->createRequest('POST', $url) 97 ->withHeader('Content-Type', 'application/x-www-form-urlencoded') 98 ; 99 100 // some enpoints may require a basic auth header here 101 $request = $this->setRequestBody($body, $request); 102 103 return $this->http->sendRequest($request); 104 } 105 106}