friendship ended with social-app. php is my new best friend
1<?php 2/** 3 * Class SessionStorage 4 * 5 * @created 09.07.2017 6 * @author Smiley <smiley@chillerlan.net> 7 * @copyright 2017 Smiley 8 * @license MIT 9 */ 10declare(strict_types=1); 11 12namespace chillerlan\OAuth\Storage; 13 14use chillerlan\OAuth\OAuthOptions; 15use chillerlan\OAuth\Core\AccessToken; 16use chillerlan\Settings\SettingsContainerInterface; 17use Psr\Log\{LoggerInterface, NullLogger}; 18use function session_start, session_status, session_write_close; 19use const PHP_SESSION_ACTIVE, PHP_SESSION_DISABLED; 20 21/** 22 * Implements a session storage adapter. 23 * 24 * Note: the session storage is only half persistent, as tokens are stored for the duration of the session. 25 */ 26class SessionStorage extends OAuthStorageAbstract{ 27 28 /** 29 * the key name for the storage array in $_SESSION 30 */ 31 protected string $storageVar; 32 33 public function __construct( 34 OAuthOptions|SettingsContainerInterface $options = new OAuthOptions, 35 LoggerInterface $logger = new NullLogger, 36 ){ 37 parent::__construct($options, $logger); 38 39 $this->storageVar = $this->options->sessionStorageVar; 40 41 // Determine if the session has started. 42 $status = session_status(); 43 44 if($this->options->sessionStart && $status !== PHP_SESSION_DISABLED && $status !== PHP_SESSION_ACTIVE){ 45 session_start(); 46 } 47 48 if(!isset($_SESSION[$this->storageVar])){ 49 $_SESSION[$this->storageVar] = [ 50 $this::KEY_TOKEN => [], 51 $this::KEY_STATE => [], 52 $this::KEY_VERIFIER => [], 53 ]; 54 } 55 56 } 57 58 /** 59 * SessionStorage destructor. 60 * 61 * @codeCoverageIgnore 62 */ 63 public function __destruct(){ 64 if($this->options->sessionStop && session_status() === PHP_SESSION_ACTIVE){ 65 session_write_close(); 66 } 67 } 68 69 70 /* 71 * Access token 72 */ 73 74 public function storeAccessToken(AccessToken $token, string $provider):static{ 75 $_SESSION[$this->storageVar][$this::KEY_TOKEN][$this->getProviderName($provider)] = $this->toStorage($token); 76 77 return $this; 78 } 79 80 public function getAccessToken(string $provider):AccessToken{ 81 82 if($this->hasAccessToken($provider)){ 83 return $this->fromStorage($_SESSION[$this->storageVar][$this::KEY_TOKEN][$this->getProviderName($provider)]); 84 } 85 86 throw new ItemNotFoundException($this::KEY_TOKEN); 87 } 88 89 public function hasAccessToken(string $provider):bool{ 90 return !empty($_SESSION[$this->storageVar][$this::KEY_TOKEN][$this->getProviderName($provider)]); 91 } 92 93 public function clearAccessToken(string $provider):static{ 94 unset($_SESSION[$this->storageVar][$this::KEY_TOKEN][$this->getProviderName($provider)]); 95 96 return $this; 97 } 98 99 public function clearAllAccessTokens():static{ 100 $_SESSION[$this->storageVar][$this::KEY_TOKEN] = []; 101 102 return $this; 103 } 104 105 106 /* 107 * CSRF state 108 */ 109 110 public function storeCSRFState(string $state, string $provider):static{ 111 112 if($this->options->useStorageEncryption === true){ 113 $state = $this->encrypt($state); 114 } 115 116 $_SESSION[$this->storageVar][$this::KEY_STATE][$this->getProviderName($provider)] = $state; 117 118 return $this; 119 } 120 121 public function getCSRFState(string $provider):string{ 122 123 if(!$this->hasCSRFState($provider)){ 124 throw new ItemNotFoundException($this::KEY_STATE); 125 } 126 127 $state = $_SESSION[$this->storageVar][$this::KEY_STATE][$this->getProviderName($provider)]; 128 129 if($this->options->useStorageEncryption === true){ 130 return $this->decrypt($state); 131 } 132 133 return $state; 134 } 135 136 public function hasCSRFState(string $provider):bool{ 137 return !empty($_SESSION[$this->storageVar][$this::KEY_STATE][$this->getProviderName($provider)]); 138 } 139 140 public function clearCSRFState(string $provider):static{ 141 unset($_SESSION[$this->storageVar][$this::KEY_STATE][$this->getProviderName($provider)]); 142 143 return $this; 144 } 145 146 public function clearAllCSRFStates():static{ 147 $_SESSION[$this->storageVar][$this::KEY_STATE] = []; 148 149 return $this; 150 } 151 152 153 /* 154 * PKCE verifier 155 */ 156 157 public function storeCodeVerifier(string $verifier, string $provider):static{ 158 159 if($this->options->useStorageEncryption === true){ 160 $verifier = $this->encrypt($verifier); 161 } 162 163 $_SESSION[$this->storageVar][$this::KEY_VERIFIER][$this->getProviderName($provider)] = $verifier; 164 165 return $this; 166 } 167 168 public function getCodeVerifier(string $provider):string{ 169 170 if(!$this->hasCodeVerifier($provider)){ 171 throw new ItemNotFoundException($this::KEY_VERIFIER); 172 } 173 174 $verifier = $_SESSION[$this->storageVar][$this::KEY_VERIFIER][$this->getProviderName($provider)]; 175 176 if($this->options->useStorageEncryption === true){ 177 return $this->decrypt($verifier); 178 } 179 180 return $verifier; 181 } 182 183 public function hasCodeVerifier(string $provider):bool{ 184 return !empty($_SESSION[$this->storageVar][$this::KEY_VERIFIER][$this->getProviderName($provider)]); 185 } 186 187 public function clearCodeVerifier(string $provider):static{ 188 unset($_SESSION[$this->storageVar][$this::KEY_VERIFIER][$this->getProviderName($provider)]); 189 190 return $this; 191 } 192 193 public function clearAllCodeVerifiers():static{ 194 $_SESSION[$this->storageVar][$this::KEY_VERIFIER] = []; 195 196 return $this; 197 } 198 199}