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}